Fichier de configuration pour Python avec ConfigParser

Il y a de multiples raisons de mettre en place un fichier de configuration pour une application. Nous allons voir comment faire cela grâce à un module de python appelé ConfigParser qui sert… à parser une configuration de façon très simple.

Pourquoi un fichier de configuration ?

Utiliser un fichier de configuration permet d’obtenir une application plus souple, si vous faites un choix dans le programme, demandez vous s’il ne serait pas plus efficace de le mettre en tant que paramètre, modifiable par l’utilisateur. Il est plus agréable de les manipuler de façon séparé, et surtout évite qu’un utilisateur change un mauvais paramètre par mégarde dans votre code source.

Présentation du module ConfigParser

Puisqu’un module existe pour Python, autant l’utiliser. Un des avantages est d’avoir la possibilité de commenter vos configuration en commencent par # ou ; une ligne, celle-ci sera ignoré. Le fichier suit une structure assez simple, basé sur un système de section, et d’argument/valeurs. Pour résumer dans un schéma :

[my_section]
coin = pan
answer = 42

On peut de la sorte avoir plusieurs noms de section et ce n’est là que pour donner une idée basique du module.

Lire dans un fichier de configuration

Premier exemple tout simple, nous allons lire dans un fichier quelques arguments, pour les afficher en retour dans la console. Par commodité, on va placer le fichier de configuration à l’endroit où sera exécuté le programme. Le nom du fichier serra ici conf.rc mais aucune importance, du moment qu’on s’y retrouve.

conf.rc

[user]
nom = Dupond
age = 42

Il nous reste plus qu’a écrire le bout de code en python pour interpréter ça.

config.py

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import ConfigParser

def main ():

    config = ConfigParser.RawConfigParser()
    config.read('conf.rc')

    nom = config.get('user', 'nom')
    age = config.get('user', 'age')

    print 'Bonjour M. %s, vous avez %s ans' % (nom, age)

if __name__ == '__main__':
    main()

On le rend exécutable:

chmod +x config.py

Puis on le lance:

$ ./config.py  
Bonjour M. Dupond, vous avez 42 ans

Ça fait ce qu’on voulais, mais voici quelques explications :

#!/usr/bin/env python2.7

Étant sous Arch Linux, l’interpréteur Python par défaut est le 3.x, comme beaucoup sont avec le 2.7, j’ai explicitement mis cette indication dans le code, mais revenir à un classique #!/usr/bin/python est bon également (enfin, il ne passera pas si c’est en python3 par défaut)

import ConfigParser

Rien de bien compliqué là encore, on importe le module.

config = ConfigParser.RawConfigParser()

Sûrement la ligne la plus obscure du code, on instancie simplement un objet (config) de la classe RawConfigParser venant du module ConfigParser. Et c’est grâce à cet objet que tout va se jouer.

config.read('conf.rc')

Nous indiquons ici quel est le fichier de configuration que nous utilisons. Pour simplifier l’affaire, le fichier est en relatif (par rapport à l’endroit d’où est exécuté le programme), et surtout, il n’y a pas de vérification que le fichier existe, ni qu’il est lu correctement. Je suis sûr que vous trouverez à le faire de vous même.

nom = config.get('user', 'nom')

C’est là qu’on lit vraiment les variables, un détail à noté, tout comme on peut le deviner par la suite, la variable age est considéré comme une chaine de caractère, et non comme un nombre à part entière, dans l’état il ne serait pas possible d’effectuer des calcules dessus, mais pouvant être convertie facilement de la sorte :

age = int(config.get('user', 'age'))

De la sorte, age est maintenant effectivement un entier, et nous pourrions en faire toute sorte de calculs afin de trouver l’âge du capitaine.

Je ne pense pas que le reste à besoin de plus d’explication, plusieurs remarques cependant.

  • Le plus important, on remarque qu’il est simple d’interpréter de la sorte un fichier de configuration, le tout en une poignée de lignes seulement.
  • Il n’y a aucune gestion d’erreur ici, s’il manque un argument, ou même la section, le programme va planter lamentablement… Mais nous allons voir ça maintenant.

Gestion d’erreurs

Nous allons voir deux cas ici.

  • try/except block
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import sys
import ConfigParser

def main ():

    config = ConfigParser.RawConfigParser()
    config.read('conf.rc')

    try:
        nom = config.get('user', 'nom')
        age = config.get('user', 'age')
    except ConfigParser.Error, err:
        print 'Oops, une erreur dans votre fichier de conf (%s)' % err
        sys.exit(1)

    print 'Bonjour M. %s, vous avez %s ans' % (nom, age)

if __name__ == '__main__':
    main()

Et on essaye avec un fichier de config erroné suivant:

[user]
nom = Dupond

On exécute, et regarde la sortie:

$ ./config.py  
Oops, une erreur dans votre fichier de conf (No option 'age' in section:
'user')
  • has_section, has_option

Le module viens avec deux méthodes permettant de vérifier la présence de section ou d’option, on peut donc s’en servir, avec quelques choses ressemblant à ça par :

if config.has_option('user', 'nom'):
    nom = config.get('user', 'nom')
else:
    nom = 'Dude'
if config.has_option('user', 'age'):
    age = config.get('user', 'age')
else:
    age = '42'

On affecte également des valeurs par défaut si une option n’est pas trouvé, on peut noté également, que la gestion d’erreur sur la section n’est pas faite ici, uniquement les options.

Écrire dans un fichier de configuration

Jusqu’ici, nous avons vu comment lire les données d’un fichier de configuration. Pendant qu’on y est, autant jeter un œil sur la façon d’écrire, et donc sauvegarder, une configuration. Dans la lignée de ce qui à déjà était fait, même nom de fichier, même section et options.

Dans un premier temps, on supprime le fichier de configuration conf.rc si vous l’aviez gardé depuis l’exercice plus haut, et on écrit dans config.py le code suivant:

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import ConfigParser

def main ():

    config = ConfigParser.RawConfigParser()

    config.add_section('user')
    config.set('user', 'nom', 'Dupond')
    config.set('user', 'age', '42')

    with open('conf.rc', 'wb') as conf_file:
        config.write(conf_file)


if __name__ == '__main__':
    main()

On rend exécutable avec chmod +x config.py, puis on exécute le script, et on observe le résultat en ouvrant le fichier conf.rc, il contient exactement ce qu’on attendait.

Pour les explications, plus courte cette fois ci. On vois qu’on rajoute la section avec la méthode add_section, pour lequel on affecte les options avec la méthode set qui prends trois arguments :

config.set(section, options, valeur)

Sauvegarder un dictionnaire comme configuration

Imaginons que nous voulons sauvegarder un dictionnaire qui nous sert de configuration dans un fichier, on peut donc effectuer de la sorte :

#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-

import ConfigParser

def main ():

    config = ConfigParser.RawConfigParser()

    params = {
            'Linux': 'Torvalds',
            'GNU': 'RMS',
            'answer': '42',
        }

    config.add_section('params')
    for arg in params:
        config.set('params', arg, params[arg])

    with open('conf.rc', 'wb') as conf_file:
        config.write(conf_file)


if __name__ == '__main__':
    main()

On exécute, et regarde le résultat obtenu, et c’est ce que nous voulions, un fichier contenant ce dictionnaire et sous forme option = valeur.

Voilà, cette introduction au module ConfigParser touche à sa fin, C’est un module qui n’est pas compliqué à prendre en main, il est conseillé de lire la documentation fournis pour plus amples détails. En espérant motiver certain à utiliser un fichier de configuration plutôt que d’écrire « en dur » les variables directement dans le fichier source.

Si vous notez une imprécision, incohérence, ou erreur dans le code ou les explications, vous êtes invité naturellement à rajouter en commentaire.

Vus : 5450
Publié par Nicolas Paris : 149