How to ConfigParse a file keeping multiple values for identical keys?

后端 未结 5 2034
日久生厌
日久生厌 2020-12-08 17:17

I need to be able to use the ConfigParser to read multiple values for the same key. Example config file:

[test]
foo = value1
foo = value2
xxx =          


        
相关标签:
5条回答
  • 2020-12-08 17:24

    More examples Multiple values in test.cfg.

    [test]
    foo = value1
    foo = value2
     value3
    xxx = yyy
    

    <whitespace>value3 append value3 to foo list.

    ConfigParser converts the list to a string.

    /usr/lib/python2.7/ConfigParser.pyc in _read(self, fp, fpname)
        552             for name, val in options.items():
        553                 if isinstance(val, list):
    --> 554                     options[name] = '\n'.join(val)
        555 
    

    value before converting is always list or dict (MultiOrderedDict).

    Try this - with it, config.items works:

    from collections import OrderedDict
    import ConfigParser
    
    class MultiOrderedDict(OrderedDict):
        def __setitem__(self, key, value):
            if key in self:
                if isinstance(value, list):
                    self[key].extend(value)
                    return
                elif isinstance(value,str):
                    return # ignore conversion list to string (line 554)
            super(MultiOrderedDict, self).__setitem__(key, value)
    
    config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict)
    config.read(['test.cfg'])
    print config.get("test",  "foo")
    print config.get("test",  "xxx")
    print config.items("test")
    

    Outputs:

    ['value1', 'value2', 'value3']
    ['yyy']
    [('foo', ['value1', 'value2', 'value3']), ('xxx', ['yyy'])]
    

    Another implementation MultiOrderedDict

    class MultiOrderedDict(OrderedDict):
        def __setitem__(self, key, value):
            if key in self:
                if isinstance(value, list):
                    self[key].extend(value)
                    return
                elif isinstance(value,str):
                    if len(self[key])>1:
                        return
            super(MultiOrderedDict, self).__setitem__(key, value)
    

    Outputs:

    ['value1', 'value2', 'value3']
    yyy
    [('foo', ['value1', 'value2', 'value3']), ('xxx', 'yyy')]
    
    0 讨论(0)
  • 2020-12-08 17:31

    After a small modification, I was able to achieve what you want:

    class MultiOrderedDict(OrderedDict):
        def __setitem__(self, key, value):
            if isinstance(value, list) and key in self:
                self[key].extend(value)
            else:
                super(MultiOrderedDict, self).__setitem__(key, value)
                # super().__setitem__(key, value) in Python 3
    
    config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict)
    config.read(['a.txt'])
    print config.get("test",  "foo")
    print config.get("test",  "xxx")
    

    Outputs:

    ['value1', 'value2']
    ['yyy']
    
    0 讨论(0)
  • 2020-12-08 17:36

    in python 3.8 you need to also add strict=False:

    class MultiOrderedDict(OrderedDict):
        def __setitem__(self, key, value):
            if isinstance(value, list) and key in self:
                self[key].extend(value)
            else:
                super().__setitem__(key, value)
    
    config = ConfigParser.RawConfigParser(dict_type=MultiOrderedDict, strict=False)
    config.read(['a.txt'])
    print config.get("test",  "foo")
    print config.get("test",  "xxx")
    
    0 讨论(0)
  • 2020-12-08 17:43

    Just a little bit change to @abarnert's answer, otherwise it calles __setitem__ recursively, and won't stop for some reason.

    ini file:

    [section]
    key1   = value1
    key2[] = value21
    key2[] = value22
    

    Python:

    class MultiOrderedDict(OrderedDict):
        LIST_SUFFIX = '[]'
        LIST_SUFFIX_LEN = len(LIST_SUFFIX)
    
        def __setitem__(self, key, value):
            if key.endswith(self.LIST_SUFFIX):
                values = super(OrderedDict, self).setdefault(key, [])
                if isinstance(value, list):
                    values.extend(value)
                else:
                    values.append(value)
            else:
                super(MultiOrderedDict, self).__setitem__(key, value)
    
        def __getitem__(self, key):
            value = super(MultiOrderedDict, self).__getitem__(key)
            if key.endswith(self.LIST_SUFFIX) and not isinstance(value, list):
                value = value.split('\n')
            return value
    

    Test:

    def test_ini(self):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        config = RawConfigParser(dict_type=MultiOrderedDict, strict=False)
        config.readfp(codecs.open('{}/../config/sample.ini'.format(dir_path), encoding="utf_8_sig"))
        self.assertEquals(config.get("section1", "key1"), 'value1')
        self.assertEquals(config.get("section1", "key2[]"), ['value21', 'value22'])
    
    0 讨论(0)
  • 2020-12-08 17:45

    The accepted answer breaks config.sections(), it returns always an empty list (tested with Python 3.5.3). Replacing super(OrderedDict, self).__setitem__(key, value) by super().__setitem__(key, value) fixes this, but now config.get(section, key) returns a concatenated string, no longer a list of strings.

    My solution is:

    class ConfigParserMultiValues(collections.OrderedDict):
    
        def __setitem__(self, key, value):
            if key in self and isinstance(value, list):
                self[key].extend(value)
            else:
                super().__setitem__(key, value)
    
        @staticmethod
        def getlist(value):
            return value.split(os.linesep)
    
        config = configparser.ConfigParser(strict=False, empty_lines_in_values=False, dict_type=ConfigParserMultiValues, converters={"list": ConfigParserMultiValues.getlist})
        ...
        values = config.getlist("Section", "key") # => ["value1", "value2"]
    

    The config INI file accepts duplicate keys:

    [Section]
        key = value1
        key = value2
    
    0 讨论(0)
提交回复
热议问题