【问题标题】:Export with alphabetical sort in Python ConfigParser在 Python ConfigParser 中按字母排序导出
【发布时间】:2009-12-31 09:59:10
【问题描述】:

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

即使原始/加载的配置文件是排序的,模块也会任意将section和options混合到section中,手动编辑一个巨大的未排序的配置文件真的很烦人。

PD:我正在使用 python 2.6

【问题讨论】:

    标签: python configuration-files configparser


    【解决方案1】:

    三种解决方案:

    1. 传入一个 dict 类型(构造函数的第二个参数),它以您喜欢的排序顺序返回键。
    2. 扩展类并重载write()(只需从原始源复制此方法并修改即可)。
    3. 复制文件ConfigParser.py并将排序添加到方法write()

    查看this article 获取有序字典,或者使用this implementation 保留原始添加顺序。

    【讨论】:

    • 请查看我的第四个有效解决方案的答案。
    【解决方案2】:

    这是我按字母排序编写配置文件的解决方案:

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

    【讨论】:

    • 有帮助,但未定义 DEFAULTSECT。并且会“for section in sorted(self._sections):”
    【解决方案3】:

    我可以通过从外部对 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)
    

    【讨论】:

    • 虽然这可行,但它的缺点是来自testfile.ini 的订单不会被保留。
    • 没错。我的解决方案按字母顺序对部分进行排序,并且在每个部分中所有键都按字母顺序排序。如果您只想保留原始顺序,则只需要我的解决方案的前两行。
    【解决方案4】:

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

    但是,查看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 不支持 updatecomparison 与普通 dicts。

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

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

    物品问题的其他解决方案是修改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 复制粘贴它

    那么总体来说该怎么做呢?

    1. 从之前建议的链接之一下载 odict.py
    2. 导入它。
    3. 将这些代码复制粘贴到您最喜欢的 utils.py 中(这将为您创建 OrderedRawConfigParser 类)
    4. cfg = utils.OrderedRawConfigParser(odict.OrderedDict())
    5. 一如既往地使用cfg。它将保持有序。
    6. 坐下来,抽一口哈瓦那,放松一下。

    PS3:我在这里解决的问题只存在于 Python 2.5 中。在 2.6 中已经有一个解决方案。他们在__init__ 函数中创建了第二个自定义参数,即自定义dict_type。

    所以只有 2.5 才需要这种解决方法

    【讨论】:

      【解决方案5】:

      我正在研究将 .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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-12-09
        • 2015-09-20
        • 2020-03-29
        • 2016-08-01
        • 1970-01-01
        • 2022-01-23
        • 2013-08-24
        • 1970-01-01
        相关资源
        最近更新 更多