2013-04-06 13 views
11

Devo essere in grado di utilizzare ConfigParser per leggere più valori per la stessa chiave. Esempio file di configurazione:Come configParse un file mantenendo più valori per chiavi identiche?

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

Con l'uso 'normale' di ConfigParser ci sarà una chiave foo con il valore value2. Ma ho bisogno che il parser legga in entrambi i valori.

A seguito di una entry on duplicate key ho creato il seguente codice di esempio:

from collections import OrderedDict 
from ConfigParser import RawConfigParser 

class OrderedMultisetDict(OrderedDict): 
    def __setitem__(self, key, value): 

     try: 
      item = self.__getitem__(key) 
     except KeyError: 
      super(OrderedMultisetDict, self).__setitem__(key, value) 
      return 

     print "item: ", item, value 
     if isinstance(value, list): 
      item.extend(value) 
     else: 
      item.append(value) 
     super(OrderedMultisetDict, self).__setitem__(key, item) 


config = RawConfigParser(dict_type = OrderedDict) 
config.read(["test.cfg"]) 
print config.get("test", "foo") 
print config.get("test", "xxx") 

config2 = RawConfigParser(dict_type = OrderedMultisetDict) 
config2.read(["test.cfg"]) 
print config2.get("test", "foo") 
print config.get("test", "xxx") 

La prima parte (con config) legge nella configurazione noi file 'solito', lasciando solo value2 come valore per foo (sovrascrivendo/eliminando l'altro valore) ed ottengo il seguente, uscita prevista:

value2 
yyy 

la seconda parte (config2) utilizza il mio approccio per aggiungere più valori a una Li ma l'output è invece

['value1', 'value2', 'value1\nvalue2'] 
['yyy', 'yyy'] 

Come si eliminano i valori ripetitivi? Mi aspetto un output come segue:

['value1', 'value2'] 
yyy 

o

['value1', 'value2'] 
['yyy'] 

(non mi importa se ogni valore è in una lista ...). Qualsiasi suggerimento benvenuto.

risposta

11

Dopo una piccola modifica, sono stato in grado di ottenere ciò che si vuole:

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

Uscite:

['value1', 'value2'] 
['yyy'] 
+0

Non ho idea del motivo per cui 'super (OrderedDict, auto)' opere, ma non 'super (MultiOrderedDict, auto) '. –

+0

Sì, perfetto! Tranne l'anomalia 'super'. Forse è * la * base di 'OrderedDict' quindi ... – Alex

+0

Esiste un modo per rendere questo ritorno un valore singolo invece di una lista quando c'è un solo valore? ['value1', 'value2'] e yyy invece di ['yyy'] –

-3

altri esempi più valori in test.cfg.

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

<whitespace>value3 accodamento value3 a foo lista.

ConfigParser converte l'elenco in una stringa.

/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 prima della conversione è sempre elenco o dict (MultiOrderedDict).

Prova questo - con esso, config.items opere:

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

Uscite:

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

Un'altra implementazione 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) 

Uscite:

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

Basta cambiare leggermente a @abarnert's answer, altrimenti chiama __setitem__ in modo ricorsivo e non si fermerà per qualche motivo.

file ini:

[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 

prova:

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']) 
1

La risposta accettata rompe config.sections(), restituisce sempre una lista vuota (testato con Python 3.5. 3). La sostituzione di super(OrderedDict, self).__setitem__(key, value) di super().__setitem__(key, value) risolve questo problema, ma ora config.get(section, key) restituisce una stringa concatenata, non più un elenco di stringhe.

La mia soluzione è:

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

La configurazione del file INI accetta chiavi duplicate:

[Section] 
    key = value1 
    key = value2 
+0

Ho corretto la chiamata super() sbagliata nella risposta accettata e nelle sezioni() adesso funziona. – bernie

Problemi correlati