2009-12-31 16 views
6

¿Hay alguna solución para forzar el método RawConfigParser.write() para exportar el archivo de configuración con un orden alfabético?Exportar con orden alfabético en Python ConfigParser

Incluso si el archivo de configuración original/cargado está ordenado, el módulo mezcla la sección y las opciones en las secciones arbitrariamente, y es realmente molesto editar manualmente un enorme archivo de configuración sin clasificar.

PD: Estoy usando Python 2.6

Respuesta

3

tres soluciones:

  1. Pass en un tipo dict (segundo argumento del constructor) que devuelve las llaves en el orden de clasificación preferido.
  2. Extienda la clase y sobrecargue write() (simplemente copie este método de la fuente original y modifíquelo).
  3. Copie el archivo ConfigParser.py y agregue la clasificación al método write().

Ver this article para un dict ordenado o tal vez usar this implementation que conserva el pedido añadiendo originales.

+0

favor ver mi respuesta para un cuarto, trabajando, solución. –

1

El primer método parecía la forma más fácil y segura.

Pero, después de ver el código fuente de ConfigParser, crea un dict incorporado vacío, y luego copia todos los valores del "segundo parámetro" uno por uno. Eso significa que no usará el tipo OrderedDict. Un trabajo fácil puede ser sobrecargar la clase CreateParser.

class OrderedRawConfigParser(ConfigParser.RawConfigParser): 
    def __init__(self, defaults=None): 
     self._defaults = type(defaults)() ## will be correct with all type of dict. 
     self._sections = type(defaults)() 
     if defaults: 
      for key, value in defaults.items(): 
       self._defaults[self.optionxform(key)] = value 

Deja solo un defecto abierto ... es decir, en ConfigParser.items(). odict no admite update y comparison con valores normales.

Solución (sobrecargar esta función también):

def items(self, section): 
     try: 
      d2 = self._sections[section] 
     except KeyError: 
      if section != DEFAULTSECT: 
       raise NoSectionError(section) 
      d2 = type(self._section)() ## Originally: d2 = {} 
     d = self._defaults.copy() 
     d.update(d2) ## No more unsupported dict-odict incompatibility here. 
     if "__name__" in d: 
      del d["__name__"] 
     return d.items() 

Otra solución al problema de artículos es modificar la función odict.OrderedDict.update - tal vez es más fácil que éste, pero lo dejo a ti.

PD: Implementé esta solución, pero no funciona. Si descubro que ConfigParser sigue mezclando el orden de las entradas, lo reportaré.

PS2: Resuelto. La función de lector de ConfigParser es bastante idiota. De todos modos, sólo una línea tuvo que ser cambiado - y algunos otros por una sobrecarga en un archivo externo:

def _read(self, fp, fpname): 

    cursect = None 
    optname = None 
    lineno = 0 
    e = None 
    while True: 
     line = fp.readline() 
     if not line: 
      break 
     lineno = lineno + 1 
     if line.strip() == '' or line[0] in '#;': 
      continue 
     if line.split(None, 1)[0].lower() == 'rem' and line[0] in "rR": 
      continue 
     if line[0].isspace() and cursect is not None and optname: 
      value = line.strip() 
      if value: 
       cursect[optname] = "%s\n%s" % (cursect[optname], value) 
     else: 
      mo = self.SECTCRE.match(line) 
      if mo: 
       sectname = mo.group('header') 
       if sectname in self._sections: 
        cursect = self._sections[sectname] 
       ## Add ConfigParser for external overloading 
       elif sectname == ConfigParser.DEFAULTSECT: 
        cursect = self._defaults 
       else: 
        ## The tiny single modification needed 
        cursect = type(self._sections)() ## cursect = {'__name__':sectname} 
        cursect['__name__'] = sectname 
        self._sections[sectname] = cursect 
       optname = None 
      elif cursect is None: 
       raise ConfigParser.MissingSectionHeaderError(fpname, lineno, line) 
       ## Add ConfigParser for external overloading. 
      else: 
       mo = self.OPTCRE.match(line) 
       if mo: 
        optname, vi, optval = mo.group('option', 'vi', 'value') 
        if vi in ('=', ':') and ';' in optval: 
         pos = optval.find(';') 
         if pos != -1 and optval[pos-1].isspace(): 
          optval = optval[:pos] 
        optval = optval.strip() 
        if optval == '""': 
         optval = '' 
        optname = self.optionxform(optname.rstrip()) 
        cursect[optname] = optval 
       else: 
        if not e: 
         e = ConfigParser.ParsingError(fpname) 
         ## Add ConfigParser for external overloading 
        e.append(lineno, repr(line)) 
    if e: 
     raise e 

Confía en mí, no me escribió esta cosa. Lo copié y pegué por completo desde ConfigParser.py

¿En general, qué hacer?

  1. Descargar odict.py de uno de los enlaces sugerido previamente
  2. importarlo.
  3. Copie y pegue estos códigos en sus utilidades favoritas.py (que creará la clase OrderedRawConfigParser para usted)
  4. cfg = utils.OrderedRawConfigParser(odict.OrderedDict())
  5. use cfg como siempre. se mantendrá ordenado.
  6. Siéntate, fuma una havanna, relájate.

PS3: El problema que he resuelto aquí es solo en Python 2.5. En 2.6 ya hay una solución para eso. Crearon un segundo parámetro personalizado en la función __init__, que es un dict_type personalizado.

Así que esta solución sólo se necesita el 2,5

2

Ésta es mi solución para la escritura en el archivo de configuración de clasificación alfabética:

class OrderedRawConfigParser(ConfigParser.RawConfigParser): 
""" 
Overload standart Class ConfigParser.RawConfigParser 
""" 
def __init__(self, defaults = None, dict_type = dict): 
    ConfigParser.RawConfigParser.__init__(self, defaults = None, dict_type = dict) 

def write(self, fp): 
    """Write an .ini-format representation of the configuration state.""" 
    if self._defaults: 
     fp.write("[%s]\n" % DEFAULTSECT) 
     for key in sorted(self._defaults):     
      fp.write("%s = %s\n" % (key, str(self._defaults[ key ]).replace('\n', '\n\t')))     
     fp.write("\n") 
    for section in self._sections: 
     fp.write("[%s]\n" % section) 
     for key in sorted(self._sections[section]): 
      if key != "__name__": 
       fp.write("%s = %s\n" % 
         (key, str(self._sections[section][ key ]).replace('\n', '\n\t')))  
     fp.write("\n")  
+0

Útil, pero DEFAULTSECT no está definido. Y sería "para la sección en ordenada (self._sections):" –

0

que estaba buscando en esto por la fusión de un .gitmodules haciendo un sub-árbol fusionarse con un supermódulo - estaba súper confundido desde el principio, y tener diferentes pedidos de submódulos era bastante confuso jaja.

Uso GitPython ayudó mucho:

from collections import OrderedDict 
import git 

filePath = '/tmp/git.config' 
# Could use SubmoduleConfigParser to get fancier 
c = git.GitConfigParser(filePath, False) 
c.sections() 
# http://stackoverflow.com/questions/8031418/how-to-sort-ordereddict-in-ordereddict-python 
c._sections = OrderedDict(sorted(c._sections.iteritems(), key=lambda x: x[0])) 
c.write() 
del c 
0

yo era capaz de resolver este problema mediante la clasificación de las secciones en el ConfigParser desde el exterior, así:

config = ConfigParser.ConfigParser({}, collections.OrderedDict) 
config.read('testfile.ini') 
# Order the content of each section alphabetically 
for section in config._sections: 
    config._sections[section] = collections.OrderedDict(sorted(config._sections[section].items(), key=lambda t: t[0])) 

# Order all sections alphabetically 
config._sections = collections.OrderedDict(sorted(config._sections.items(), key=lambda t: t[0])) 

# Write ini file to standard output 
config.write(sys.stdout) 
+0

Mientras esto funciona, esto tiene el inconveniente de que el orden de 'testfile.ini' no se conserva. –

+0

Eso es correcto. Mi solución ordena las secciones alfabéticamente y dentro de cada sección todas las teclas están ordenadas alfabéticamente. Si solo quiere preservar el orden original, solo se necesitan las dos primeras líneas de mi solución. –

Cuestiones relacionadas