web-dev-qa-db-fra.com

Listes dans ConfigParser

Le fichier typique généré par ConfigParser ressemble à ceci:

[Section]
bar=foo
[Section 2]
bar2= baz

Maintenant, existe-t-il un moyen d’indexer des listes comme, par exemple:

[Section 3]
barList={
    item1,
    item2
}

Question associée: Clés uniques ConfigParser de Python par section

132
pistacchio

Rien ne vous empêche de compresser la liste dans une chaîne puis de la décompresser une fois que vous obtenez la chaîne de la configuration. Si vous le faisiez de cette façon, votre section de configuration ressemblerait à ceci:

[Section 3]
barList=item1,item2

Ce n'est pas joli mais c'est fonctionnel pour la plupart des listes simples.

108
David Locke

Également un peu en retard, mais peut-être utile pour certains… .. J'utilise une combinaison de ConfigParser et JSON:

[Foo]
fibs: [1,1,2,3,5,8,13]

il suffit de le lire avec:

>>> json.loads(config.get("Foo","fibs"))
[1, 1, 2, 3, 5, 8, 13]

Vous pouvez même casser des lignes si votre liste est longue (merci @ peter-smit):

[Bar]
files_to_check = [
     "/path/to/file1",
     "/path/to/file2",
     "/path/to/another file with space in the name"
     ]

Bien sûr, je pourrais simplement utiliser JSON, mais je trouve les fichiers de configuration beaucoup plus lisibles et la section [DEFAULT] très pratique.

171
quasimodo

Arriver en retard à cette soirée, mais j'ai récemment implémenté ceci avec une section dédiée dans un fichier de configuration pour une liste:

[paths]
path1           = /some/path/
path2           = /another/path/
...

et en utilisant config.items( "paths" ) pour obtenir une liste itérable d'items de chemin, comme ceci:

path_items = config.items( "paths" )
for key, path in path_items:
    #do something with path

J'espère que cela aide d'autres personnes à googler cette question;)

85
Henry Cooke

Une chose que beaucoup de gens ignorent, c'est que les valeurs de configuration multilignes sont autorisées. Par exemple:

;test.ini
[hello]
barlist = 
    item1
    item2

La valeur de config.get('hello','barlist') sera désormais:

"\nitem1\nitem2"

Ce que vous pouvez facilement diviser avec la méthode splitlines (n'oubliez pas de filtrer les éléments vides).

Si nous cherchons un gros framework comme Pyramid, ils utilisent cette technique:

def aslist_cronly(value):
    if isinstance(value, string_types):
        value = filter(None, [x.strip() for x in value.splitlines()])
    return list(value)

def aslist(value, flatten=True):
    """ Return a list of strings, separating the input based on newlines
    and, if flatten=True (the default), also split on spaces within
    each line."""
    values = aslist_cronly(value)
    if not flatten:
        return values
    result = []
    for value in values:
        subvalues = value.split()
        result.extend(subvalues)
    return result

La source

Moi-même, je pourrais peut-être prolonger ConfigParser s'il s'agit d'une chose courante pour vous:

class MyConfigParser(ConfigParser):
    def getlist(self,section,option):
        value = self.get(section,option)
        return list(filter(None, (x.strip() for x in value.splitlines())))

    def getlistint(self,section,option):
        return [int(x) for x in self.getlist(section,option)]

Notez qu'il y a quelques points à surveiller lors de l'utilisation de cette technique

  1. Les nouvelles lignes qui sont des éléments doivent commencer par un espace (par exemple, un espace ou un onglet)
  2. Toutes les lignes suivantes commençant par des espaces sont considérées comme faisant partie de l'élément précédent. Aussi, si elle a un signe = ou si elle commence par un; en suivant les espaces.
56
Peter Smit

Si vous voulez littéralement passer une liste, vous pouvez utiliser:

ast.literal_eval()

Par exemple configuration:

[section]
option=["item1","item2","item3"]

Le code est:

import ConfigParser
import ast

my_list = ast.literal_eval(config.get("section", "option"))
print(type(my_list))
print(my_list)

sortie:

<type'list'>
["item1","item2","item3"]
27
PythonTester

J'ai atterri ici pour chercher à consommer cette ...

[global]
spys = [email protected], [email protected]

La réponse est de la diviser sur la virgule et de supprimer les espaces:

SPYS = [e.strip() for e in parser.get('global', 'spys').split(',')]

Pour obtenir un résultat de liste:

['[email protected]', '[email protected]']

Cela peut ne pas répondre exactement à la question du PO mais peut-être être la réponse simple que certaines personnes recherchent.

14
John Mee

C'est ce que j'utilise pour les listes:

contenu du fichier de configuration: 

[sect]
alist = a
        b
        c

code: 

l = config.get('sect', 'alist').split('\n')

ça marche pour les ficelles

en cas de nombre 

contenu de la configuration:

nlist = 1
        2
        3

code:

nl = config.get('sect', 'alist').split('\n')
l = [int(nl) for x in nl]

merci.

8
LittleEaster

Aucune mention de la converters KWARG POUR ConfigParser() dans aucune de ces réponses n’a été décevante. 

Selon la documentation, vous pouvez passer un dictionnaire à ConfigParser qui ajoutera une méthode get à la fois pour l’analyseur et les proxies de section. Donc pour une liste:

exemple.ini

[Germ]
germs: a,list,of,names, and,1,2, 3,numbers

Exemple de parseur:

cp = ConfigParser(converters={'list': lambda x: [i.strip() for i in x.split(',')]})
cp.read('example.ini')
cp.getlist('Germ', 'germs')
['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']
cp['Germ'].getlist('germs')
['a', 'list', 'of', 'names', 'and', '1', '2', '3', 'numbers']

C'est mon choix personnel, car aucun sous-classement n'est nécessaire et je n'ai pas à compter sur un utilisateur final pour écrire parfaitement JSON ou une liste pouvant être interprétée par ast.literal_eval

6
Grr

Seuls les types primitifs sont pris en charge pour la sérialisation par l'analyseur de configuration. J'utiliserais JSON ou YAML pour ce genre d'exigence.

2
M. Utku ALTINKAYA

J'ai rencontré le même problème dans le passé. Si vous avez besoin de listes plus complexes, créez votre propre analyseur en héritant de ConfigParser. Ensuite, vous écraseriez la méthode get avec ceci:

    def get(self, section, option):
    """ Get a parameter
    if the returning value is a list, convert string value to a python list"""
    value = SafeConfigParser.get(self, section, option)
    if (value[0] == "[") and (value[-1] == "]"):
        return eval(value)
    else:
        return value

Avec cette solution, vous pourrez également définir des dictionnaires dans votre fichier de configuration.

Mais fais attention! Ce n'est pas aussi sûr: cela signifie que n'importe qui pourrait exécuter du code dans votre fichier de configuration. Si la sécurité n’est pas un problème dans votre projet, j’envisagerais d’utiliser directement les classes python en tant que fichiers de configuration. Ce qui suit est beaucoup plus puissant et consommable qu'un fichier ConfigParser:

class Section
    bar = foo
class Section2
    bar2 = baz
class Section3
    barList=[ item1, item2 ]
2
Mapad
import ConfigParser
import os

class Parser(object):
    """attributes may need additional manipulation"""
    def __init__(self, section):
        """section to retun all options on, formatted as an object
        transforms all comma-delimited options to lists
        comma-delimited lists with colons are transformed to dicts
        dicts will have values expressed as lists, no matter the length
        """
        c = ConfigParser.RawConfigParser()
        c.read(os.path.join(os.path.dirname(__file__), 'config.cfg'))

        self.section_name = section

        self.__dict__.update({k:v for k, v in c.items(section)})

        #transform all ',' into lists, all ':' into dicts
        for key, value in self.__dict__.items():
            if value.find(':') > 0:
                #dict
                vals = value.split(',')
                dicts = [{k:v} for k, v in [d.split(':') for d in vals]]
                merged = {}
                for d in dicts:
                    for k, v in d.items():
                        merged.setdefault(k, []).append(v)
                self.__dict__[key] = merged
            Elif value.find(',') > 0:
                #list
                self.__dict__[key] = value.split(',')

Alors maintenant, mon fichier config.cfg, qui pourrait ressembler à ceci:

[server]
credentials=username:admin,password:$3<r3t
loggingdirs=/tmp/logs,~/logs,/var/lib/www/logs
timeoutwait=15

Peut être analysé en objets suffisamment fins pour mon petit projet.

>>> import config
>>> my_server = config.Parser('server')
>>> my_server.credentials
{'username': ['admin'], 'password', ['$3<r3t']}
>>> my_server.loggingdirs:
['/tmp/logs', '~/logs', '/var/lib/www/logs']
>>> my_server.timeoutwait
'15'

Ceci permet une analyse très rapide des configurations simples, vous perdez toute possibilité d'extraire des ints, des bools et d'autres types de sortie sans transformer l'objet renvoyé de Parser, ni refaire le travail d'analyse effectué par la classe Parser ailleurs.

1
Droogans

J'ai terminé une tâche similaire dans mon projet avec une section avec des clés sans valeurs:

import configparser

# allow_no_value param says that no value keys are ok
config = configparser.ConfigParser(allow_no_value=True)

# overwrite optionxform method for overriding default behaviour (I didn't want lowercased keys)
config.optionxform = lambda optionstr: optionstr

config.read('./app.config')

features = list(self.config['FEATURES'].keys())

print(features)

Sortie:

['BIOtag', 'TextPosition', 'IsNoun', 'IsNomn']

app.config:

[FEATURES]
BIOtag
TextPosition
IsNoun
IsNomn
0
feeeper

json.loads & ast.literal_eval semble fonctionner, mais une simple liste dans config traite chaque caractère comme un octet renvoyant ainsi les crochets même ....

signification si config a fieldvalue = [1,2,3,4,5]

then config.read(*.cfg)config['fieldValue'][0] retournant [ à la place de 1

0
Abhishek Jain