Outils pour utilisateurs

Outils du site


issue145:python

There are times when you need a number of variables initialized at startup based on the last time the program ran. In the world of Windows, this is usually saved in a configuration file with a “.ini” extension. There are a number of ways to do this. One would be an XML file or even a database. However, both of these are often overkill and not quick and easy solutions. There is a library that can help us, however, in Python.

Il y a des fois où vous avez besoin qu'un certain nombre de variables soient initialisées au démarrage en vous basant sur la dernière fois où le programme a tourné. Dans le monde de Windows, c'est en général enregistré dans un fichier de configuration avec l'extension « .ini ». Il y a de multiples façons de le faire. Ce pourrait être un fichier XML ou même une base de données. Cependant, ces deux solutions sont souvent exagérées, et ni rapides ni faciles. Une bibliothèque en Python peut, cependant, vous aider.

Installation The library is configparser, and is easily installed with pip: pip3 install configparser Notice that I used pip3 rather than pip. If you are still using Python2, you should use pip, but since Python 2.x is ending life on Jan. 1, 2020, I've decided that I'm going to use only Python 3.x syntax. The current version is 3.7.4 (according to the PyPI site (https://pypi.org/project/configparser/) and was last updated on March 22, 2019. This version is compatible with Python 2.6 onwards, including Python 3.7. There was an earlier version for 2.6-3.5 called ConfigParser which has been around for many years. If you want to use the older version, you can import it directly as a backport: from backports import configparser Otherwise, you would import it as normal: import configparser

Installation

La bibliothèque est configparser, qui est facile à installer avec pip :

pip3 install configparser

Notez que j'ai utilisé pip3 plutôt que pip. Si vous utilisez toujours Python 2, vous devrez utiliser pip, mais, comme Python 2.x arrivera en fin de vie le 1er janvier 2020, j'ai décidé de n'utiliser désormais que la syntaxe de Python 3.x. La version actuelle est la 3.7.4 (d'après le site PyPI - https://pypi.org/project/configparser/) et la dernière mise à jour date du 22 mars 2019. Cette version est compatible avec Python 2.6, jusqu'à Python 3.7. Une version précédente existait pour les 2.6-3.5, appelée ConfigParser, qui est là depuis de nombreuses années. Si vous voulez utiliser l'ancienne version, vous pouvez l'importer directement comme un backport :

from backports import configparser

Autrement, vous l'importerez normalement :

import configparser

Use The INI file (which is NOT compatible with Windows “official” .ini file format) is a simple text file. You can use the extension .ini, or .cfg, or whatever you want. In this tutorial however, I’ll use .ini as the extension. This file consists of key/value entries that are grouped by sections that are blocked with a [section] header. By default, the section names are case sensitive, but keys are not. Leading and trailing whitespace is remove from both keys and values. The configuration file may also include comments that are, by default, only on an empty line. Inline comments can be used, but might cause problems, so I suggest that you don't use them. Comments, as in Python, start with a “#”. A very gross idea for the layout of the .ini file would be something like this…

Utilisation

Le fichier INI (qui n'est PAS compatible avec le format de fichier « officiel » .ini de Windows) est un simple fichier texte. Vous pouvez utiliser l'extension .ini ou .cfg, ou ce que vous voulez. Dans ce tutoriel, cependant, j'utiliserai .ini comme extension. Ce fichier est composé d'entrées clé/valeur qui sont groupées par sections qui sont marquées avec une entête [block]. Par défaut, les noms de section sont sensibles à la casse, mais pas les clés. Les blancs de début et de fin sont supprimés des clés comme des valeurs. Le fichier de configuration peut aussi contenir des commentaires qui sont, par défaut, seulement sur une ligne vide. Les commentaires alignés peuvent être utilisés, mais peuvent causer des problèmes ; aussi, je vous suggère de ne pas les utiliser. Comme en Python, les commentaires commencent par un « # ».

Une idée très grossière de la disposition du fichier .ini pourrait être quelque chose comme ceci :

[Animals] animal1 = Frog animal2 = Dog animal3 = Hog [Trees] tree1 = The Larch tree2 = Elm tree3 = Ash In this example, we have two sections, 'Animals' and 'Trees'. Each section contains three variables (animal1, animal2, etc) which are our keys and each has a value. You can also define a key without a default value: tree4 = However, when you use the 'tree4' variable, it is a blank string, not None. The library doesn't try to guess what datatype a variable is. It always stores them internally as string.

[Animals] # Animaux animal1 = Frog # Grenouille animal2 = Dog # Chien animal3 = Hog # Porc

[Trees] # Arbres tree1 = The Larch # Mélèze tree2 = Elm # Orme tree3 = Ash # Frêne

Dans cet exemple, nous avons deux sections, « Animals » et « Trees ». Chaque section contient trois variables (animal1, animal 2, etc.) qui sont nos clés et chacune a une valeur. Vous pouvez aussi définir une clé sans valeur par défaut :

tree4 =

Cependant, quand vous utilisez la variable « tree4 », c'est une chaîne vierge, et pas None (rien).

La bibliothèque n'essaie pas de deviner quel est le type d'une variable. Elle les stocke toujours en interne comme des chaînes.

The Code Now we'll get into the code that we would use to read, write and create a default INI file. We'll name this program “iniFile.py”. We'll start with the imports section: import os import configparser In this simple demo program, we only need two imports, os and configparser. You'll see why we want the os library in a moment. Now we will define a global variable “iniFineName”, which holds the filename of our ini file.

Le code

Maintenant, nous allons rentrer dans le code que nous utiliserons pour lire, écrire et créer un fichier INI par défaut. Nous nommerons ce programme « iniFile.py ». Nous commencerons avec la section imports :

import os import configparser

Dans ce simple programme de démo, nous n'avons besoin que de deux imports, os et configparser. Nous verrons dans un moment pourquoi nous voulons la bibliothèque os. Maintenant, nous définirons une variable globale « iniFileName, qui contient le nom de fichier de notre fichier ini.

global iniFileName iniFileName = “MyConfigFile.ini” Now, we'll create a function to read our ini file: def read_ini(): global ini, iniFileName global tree1, tree2, tree3, tree4 global animal1, animal2, animal3 global theanswer

global iniFileName

iniFileName = “MyConfigFile.ini”

Maintenant, nous créerons une fonction pour lire notre fichier ini :

def read_ini():

  global ini, iniFileName
  global tree1, tree2, tree3, tree4
  global animal1, animal2, animal3
  global theanswer

We define a number of global variables just to make things easy. Then, we check to see if the file exists (the os.path.isfile() method), and then read the file: if os.path.isfile(iniFileName): ini.read(iniFileName) This next bit of code (top right) shows how we can view the various sections and key/value sets.

Nous définissons un certain nombre de variables globales pour faciliter les choses. Ensuite, nous vérifions pour voir si le fichier existe (la méthode os.path.isfile()), puis nous lisons le fichier :

if os.path.isfile(iniFileName):

ini.read(iniFileName)

Le prochain morceau de code (en haut à droite) montre comment nous pouvons voir les diverses sections et les ensembles clé/valeur.

Now we can assign the values to the proper variables: animals = ini['Animals'] animal1 = animals['animal1'] animal2 = animals['animal2'] animal3 = animals['animal3'] trees = ini['Trees'] tree1 = trees['tree1'] tree2 = trees['tree2'] tree3 = trees['tree3'] tree4 = trees['tree4'] ans = ini['Answers'] We can also use the .get method of the section object to assign the value to a variable: theanswer = ans.get('Life, The Universe and Everything')

Maintenant, nous pouvons assigner les valeurs aux variables appropriées :

animals = ini['Animals'] animal1 = animals['animal1'] animal2 = animals['animal2'] animal3 = animals['animal3'] trees = ini['Trees'] tree1 = trees['tree1'] tree2 = trees['tree2'] tree3 = trees['tree3'] tree4 = trees['tree4'] ans = ini['Answers']

Nous pouvons aussi utiliser la méthode .get de l'objet de la section pour assigner une valeur à une variable :

  	theanswer = ans.get('Life, The Universe and Everything')

Now, we return 'True' to the call to say that the INI file existed. Otherwise, since the INI file doesn't exist at this point, we'll use some default values to create one and return 'False' to force the calling routine to re-read the INI file: return(True) else: write_default_ini() return(False)

Maintenant, nous retournons « True » (vrai) lors d'un appel pour dire que le fichier INI existe. Sinon, comme le fichier INI n'existe pas à cet instant, nous utiliserons des valeurs pour en créer un et retourner « False » (faux) pour forcer la routine appelante à lire à nouveau le fichier INI :

      return(True)
  else:
  	 write_default_ini()
      return(False)

Now, here is the function that can write to the INI file. In this case, we'll only write one value, but this will show how to do it. Basically, we use the .set(section,key,value) to update the ini object, then write it back out properly. def write_ini(): global ini, iniFileName ini.set('Trees', 'tree4', tree4) ini.write(open(iniFileName, 'w')) Here (bottom right) is the function to write a default INI file, just in case it doesn't exist. We use object.add_section() to create a section and object.set() to add a key/value under that section.

Voici maintenant la fonction qui peut lire le fichier INI. Dans notre cas, nous n'écrirons qu'une valeur, mais ça vous montrera comment faire. En gros, nous utilisons .set(section,key,value) pour mettre à jour l'objet ini, puis, nous le ré-enregistrons proprement.

def write_ini():

  global ini, iniFileName
  ini.set('Trees', 'tree4', tree4)
  ini.write(open(iniFileName, 'w'))
  

Voici (en bas à droite) la fonction pour écrire un fichier INI par défaut, juste dans le cas où il n'existe pas. Nous utilisons object.add_section() pour créer une section et object.set() pour ajouter un ensemble clé/valeur dans cette section.

Finally, we write our configuration file to disk, using the global iniFileName that we set up earlier at the top of the program: # Writing our configuration file with open(iniFileName, 'w') as configfile: config.write(configfile) This function (next page, top right) simply is used to display all the variables that were pulled from the INI file: The function init() (next page, bottom right) is where the real work is done; we instantiate the config parser object as 'ini', read the INI file and check to see if we get a 'True' (it is there) or 'False' (we needed to create it from defaults), so try to read again, show the variables, then update a value (tree4) then write the changed variable back to the file:

Enfin, nous écrivons notre fichier de configuration sur le disque, en utilisant la variable globale iniFileName que nous avons paramétré précédemment au début du programme :

  # Writing our configuration file
  with open(iniFileName, 'w') as configfile:
      config.write(configfile)
      

Cette fonction (page suivante, en haut à droite) est utilisée pour afficher toutes les variables qui ont été tirées du fichier INI.

C'est dans la fonction init() (page suivante, en bas à droite) que tout le travail effectif s'opère : nous instancions l'objet configparser comme « ini », lisons le fichier INI et vérifions pour voir si nous recevons « True » (il est là) ou « False » (nous devons en créer un à partir des valeurs par défaut) ; dans ce cas, nous essayons de le lire à nouveau, montrons les variables puis mettons à jour une valeur (tree4) et, ensuite, nous réécrivons la variable modifiée dans le fichier.

Finally, we have our “if name” entry point to our program which calls the init() function, and when it's done, notify the user that we are all done: if name == 'main': # ========================== # All code is run from the init() function # ========================== init() # Notify user that we are done print('Program End') That's all there is to it. Here's what the output looks like the first time the program is run: ['Animals', 'Trees', 'Answers'] Section: Animals Key = animal1 - Value = Frog Key = animal2 - Value = Dog Key = animal3 - Value = Hog Section: Trees Key = tree1 - Value = The Larch Key = tree2 - Value = Elm Key = tree3 - Value = Ash Key = tree4 - Value = Section: Answers Key = life, the universe and everything - Value = 42 animal1: Frog, animal2: Dog, animal3: Hog tree1: The Larch, tree2: Elm, tree3: Ash, tree4: What's the answer to Life, The Universe and Everything? 42 theanswer type is <class 'str'> Program End

Enfin, nous avons notre point d'entrée « if name » dans notre programme qui appelle la fonction init() et, quand c'est fait, notifie à l'utilisateur que tout est terminé :

if name == 'main': #

# All code is run from the init() function #

  init()

# Notify user that we are done

  print('Program End')
  

C'est tout ce qu'il y a dire là-dessus. Voici à quoi ressemble la sortie la première fois que le progamme tourne :

['Animals', 'Trees', 'Answers'] Section: Animals Key = animal1 - Value = Frog Key = animal2 - Value = Dog Key = animal3 - Value = Hog Section: Trees Key = tree1 - Value = The Larch Key = tree2 - Value = Elm Key = tree3 - Value = Ash Key = tree4 - Value = Section: Answers Key = life, the universe and everything - Value = 42 animal1: Frog, animal2: Dog, animal3: Hog tree1: The Larch, tree2: Elm, tree3: Ash, tree4: What's the answer to Life, The Universe and Everything? 42 theanswer type is <class 'str'> Program End

Notice that the value for “tree4” is blank. If, however, you look at the INI file, it looks like this: [Animals] animal1 = Frog animal2 = Dog animal3 = Hog [Trees] tree1 = The Larch tree2 = Elm tree3 = Ash tree4 = Birch [Answers] life, the universe and everything = 42

Notez que la valeur de « tree4 » est vierge. Cependant, si vous regardez le fichier INI, il ressemble à ceci :

[Animals] animal1 = Frog animal2 = Dog animal3 = Hog

[Trees] tree1 = The Larch tree2 = Elm tree3 = Ash tree4 = Birch

[Answers] life, the universe and everything = 42

That's because the last line of the init() function updates the “tree4” variable to Birch in the write_ini() function. I didn't get too deep into the possibilities of this library, but if you want to learn more, you can read about it from the official Python docs on configparser at https://docs.python.org/3/library/configparser.html. I've put the code example for this month on pastebin at https://pastebin.com/X37remDa. Until next time, keep coding and have a great month!

C'est parce que la dernière ligne de la fonction init() met à jour la variable « tree4 » avec la valeur Birch (bouleau) dans la fonction write_ini().

Je n'ai pas cherché trop loin dans les possibilités de cette bibliothèque, mais si vous voulez en apprendre davantage, vous pouvez lire sur configparser dans la documentation officielle de Python à https://docs.python.org/3/library/configparser.html.

J'ai mis l'exemple de code de ce mois sur pastebin à https://pastebin.com/X37remDa.

Jusqu'à la prochaine fois, continuez à coder et passez un excellent mois !

issue145/python.txt · Dernière modification : 2019/06/11 11:21 de auntiee