Python Config Parser (Duplicate Key Support)

Así que recientemente comencé a escribir un analizador de configuración para un proyecto de Python en el que estoy trabajando. Inicialmente evité configparser y configobj, porque quería admitir un archivo de configuración así:

key=value key2=anothervalue food=burger food=hotdog food=cake icecream 

En resumen, este archivo de configuración será editado a través de la línea de comandos a través de SSH a menudo. Por lo tanto, no quiero tabular ni me refiero al espaciado (como YAML), pero también quiero evitar claves con múltiples valores (fácilmente 10 o más) que estén envueltos en la línea en vi. Es por eso que me gustaría apoyar claves duplicadas.

Un mundo ideal, cuando le pregunto al objeto de configuración de Python para la comida, me devolvería una lista con [‘burger’, ‘hotdog’, ‘cake’, ‘icecream’]. Si no hubiera un valor de alimento definido, buscaría en un archivo de configuración predeterminado y me daría ese / esos valores.

Ya he implementado lo anterior.

Sin embargo, mis problemas comenzaron cuando me di cuenta de que quería apoyar la conservación de los comentarios en línea y demás. La forma en que manejo la lectura y escritura en los archivos de configuración es decodificar el archivo en un dictado en la memoria, leer los valores del dict, o escribir valores en el dict, y luego volcar ese dictado nuevamente en un archivo. Esto no es realmente bueno para preservar el orden de las líneas y los comentarios, y eso me está fastidiando.

A) ConfigObj parece que tiene todo lo que necesito, excepto el soporte de claves duplicadas. En su lugar, quiere que haga una lista que será un problema editar manualmente en vi sobre ssh debido al ajuste de línea. ¿Puedo hacer que configobj sea más amigable con ssh / vi?

B) ¿Está mal mi solución casera? ¿Hay una mejor manera de leer / escribir / almacenar mis valores de configuración? ¿Hay alguna manera fácil de manejar el cambio de un valor clave en un archivo de configuración simplemente modificando esa línea y reescribiendo todo el archivo de configuración de la memoria?

Bueno, ciertamente intentaría aprovechar lo que está en la biblioteca estándar si pudiera.

La firma para las clases del analizador de configuración se ve así:

class ConfigParser.SafeConfigParser([defaults[, dict_type[, allow_no_value]]])

Note el argumento dict_type . Cuando se proporcione, se usará para construir los objetos del diccionario para la lista de secciones, para las opciones dentro de una sección y para los valores predeterminados. El valor predeterminado es para collections.OrderedDict . Quizás podría pasar algo allí para obtener el comportamiento deseado de varias claves y luego aprovechar todas las ventajas de ConfigParser . Puede que tenga que escribir su propia clase para hacer esto, o posiblemente encuentre una escrita para usted en PyPi o en las recetas de ActiveState. Intenta buscar una bolsa o clase multiset.

O iría por esa ruta o simplemente la chuparía y haría una lista:

 foo = value1, value2, value3 

Idea loca: haga los valores de su diccionario como una lista de 3 tuplas con número de línea, número de columna y valor en sí, y agregue una clave especial para comentar.

 CommentSymbol = ';' def readConfig(filename): f = open(filename, 'r') if not f: return def addValue(dict, key, lineIdx, colIdx, value): if key in dict: dict[key].append((lineIdx, colIdx, value)) else: dict[key] = [(lineIdx, colIdx, value)] res = {} i = 0 for line in f.readlines(): idx = line.find(CommentSymbol) if idx != -1: comment = line[idx + 1:] addValue(res, CommentSymbol, i, idx, comment) line = line[:idx] pair = [x.strip() for x in line.split('=')][:2] if len(pair) == 2: addValue(res, pair[0], i, 0, pair[1]) i += 1 return res def writeConfig(dict, filename): f = open(filename, 'w') if not f: return index = sorted(dict.iteritems(), cmp = lambda x, y: cmp(x[1][:2], y[1][:2])) i = 0 for k, V in index: for v in V: if v[0] > i: f.write('\n' * (v[0] - i - 1)) if k == CommentSymbol: f.write('{0}{1}'.format(CommentSymbol, str(v[2]))) else: f.write('{0} = {1}'.format(str(k), str(v[2]))) i = v[0] f.close()