在Python ConfigParser中使用字母排序导出

在Python ConfigParser中使用字母排序导出,python,configuration-files,configparser,Python,Configuration Files,Configparser,是否有任何解决方案可以强制RawConfigParser.write()方法以字母顺序导出配置文件 即使对原始/加载的配置文件进行了排序,模块也会将节和选项任意混合到节中,手动编辑一个巨大的未排序的配置文件真的很烦人 PD:我正在使用python 2.6的三种解决方案: 传入一个dict类型(第二个参数到构造函数),该类型按首选排序顺序返回键 扩展类并重载write()(只需从原始源代码复制并修改此方法) 复制文件ConfigParser.py并将排序添加到方法write() 有关保留原始添加顺

是否有任何解决方案可以强制RawConfigParser.write()方法以字母顺序导出配置文件

即使对原始/加载的配置文件进行了排序,模块也会将节和选项任意混合到节中,手动编辑一个巨大的未排序的配置文件真的很烦人

PD:我正在使用python 2.6的三种解决方案:

  • 传入一个dict类型(第二个参数到构造函数),该类型按首选排序顺序返回键
  • 扩展类并重载
    write()
    (只需从原始源代码复制并修改此方法)
  • 复制文件ConfigParser.py并将排序添加到方法
    write()

  • 有关保留原始添加顺序的有序dict或用法,请参阅。

    第一种方法看起来是最简单、更安全的方法

    但是,在查看ConfigParser的源代码之后,它会创建一个空的内置dict,然后逐个复制“第二个参数”中的所有值。这意味着它不会使用OrderedDict类型。一个简单的解决方法是重载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
    
    它只留下一个漏洞。。。即在ConfigParser.items()中。odict不支持使用普通DICT进行
    更新
    比较

    解决方法(也重载此函数):

    项目问题的另一个解决方案是修改
    odict.OrderedDict.update
    函数-可能比这个函数更简单,但我把它留给您

    PS:我实现了这个解决方案,但它不起作用。如果我发现ConfigParser仍然在混合条目的顺序,我将报告它

    PS2:解决了。ConfigParser的reader函数非常愚蠢。无论如何,只有一行需要更改,另外一些行需要在外部文件中重载:

    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
    
    相信我,这不是我写的。我完全从ConfigParser.py复制粘贴了它

    那么总体来说该怎么办呢

  • 从先前建议的链接之一下载odict.py
  • 导入它
  • 将这些代码复制粘贴到您喜爱的utils.py中(这将为您创建
    OrderedRawConfigParser
    类)
  • cfg=utils.OrderedRawConfigParser(odict.orderedict())
  • 一如既往地使用cfg。它将保持秩序
  • 坐下来,抽哈瓦那,放松
  • PS3:我在这里解决的问题只有在Python 2.5中。在2.6中已经有了解决方案。他们在
    \uuuu init\uuu
    函数中创建了第二个自定义参数,这是一种自定义dict类型


    因此,只有2.5版才需要此解决方案这是我以字母顺序编写配置文件的解决方案:

    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")    
    

    我是为了合并一个。gitmodules做一个子树合并和一个超级模块——一开始就非常混乱,并且子模块的顺序不同已经足够混乱了,哈哈

    使用GitPython有很多帮助:

    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
    

    我能够通过从外部对ConfigParser中的部分进行排序来解决此问题,如下所示:

    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)
    

    有帮助,但未定义DEFAULTSECT。“对于已排序(self._节)中的节:”请参阅我的第四个有效解决方案的答案。虽然这样做有效,但其缺点是,
    testfile.ini
    中的顺序没有保留。这是正确的。我的解决方案按字母顺序对分区进行排序,每个分区内的所有键都按字母顺序排序。如果您只想保留原始顺序,只需要我的解决方案的前两行。
    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)