Blog ENI : Toute la veille numérique !
🎁 Jusqu'au 31/12, recevez notre
offre d'abonnement à la Bibliothèque Numérique. Cliquez ici
🎁 Jusqu'au 31/12, recevez notre
offre d'abonnement à la Bibliothèque Numérique. Cliquez ici
  1. Livres et vidéos
  2. Python 3
  3. Programmation système
Extrait - Python 3 Traitement de données et techniques de programmation (2e édition)
Extraits du livre
Python 3 Traitement de données et techniques de programmation (2e édition) Revenir à la page d'achat du livre

Programmation système

Présentation

1. Définition

La programmation système se définit par opposition à la programmation d’applications. Il ne s’agit pas de concevoir un logiciel qui va utiliser le système et ses ressources pour effectuer une action, mais de concevoir une des briques qui va s’intégrer au système lui-même. Cela peut donc être le développement d’un pilote pour un matériel, d’une interface réseau ou encore la gestion des ressources.

Par extension, la création d’un programme qui utilise d’autres programmes systèmes est de la programmation système. Le terme programmation système s’étend alors à tout ce qui peut permettre à un administrateur système de résoudre les problématiques usuelles concernant son domaine de compétence, à savoir la gestion des utilisateurs, des périphériques, des processus, de la sauvegarde…

Ainsi, par extension, l’utilisation des commandes bash est de la programmation système. L’utilisation des commandes mysql et mysqldump pour opérer des actions de sauvegarde quotidiennes l’est également.

Un système d’exploitation moderne est écrit majoritairement en C, le reste étant de l’assembleur spécifique à la machine. Ce même système d’exploitation...

Appréhender son système d’exploitation

1. Avertissement

L’exécution de commandes externes est intimement liée au système sur lequel se trouve installé Python. D’une part, chaque système d’exploitation possède ses propres commandes. Par exemple, pour lister un répertoire, on utilisera ls ou dir.

Cette section traite principalement les commandes Unix.

Au-delà des commandes système classiques, certaines commandes comme mysql ou mysqldump ne peuvent être utilisées que si les programmes adéquats ont été installés, quel que soit le système.

2. Système d’exploitation

Python propose un module de bas niveau permettant de gérer des informations sur le système d’exploitation :

>>> import os 

Voici les deux principaux moyens de vérifier la nature du système :

>>> os.name 
'posix' 
>>> os.uname() 
('Linux', 'nom_donne_au_host', '2.6.38-11-generic', '#50-Ubuntu 
SMP Mon Sep 12 21:17:25 UTC 2011', 'x86_64') 

La première commande donne une standardisation de l’environnement et la seconde des détails sur le nom du système d’exploitation, de la machine, le nom du noyau et sa version ainsi que l’architecture de la machine. Tester ces valeurs permet d’effectuer des choix et d’adapter une application à un environnement précis pour certaines opérations qui le nécessitent.

Voici comment trouver la liste des variables d’environnement :

>>> list(os.environ.keys()) 

Et voici comment aller chercher la valeur d’une de ces variables :

>>> os.getenv('LANGUAGE') 
'fr_FR:fr' 

L’exploitation de ces variables d’environnement permet également de diriger des choix permettant l’adaptation de l’application. Dans le cas qui vient d’être vu, le choix de la locale peut servir à produire une interface adaptée au langage de l’utilisateur. Il est possible de lire les variables d’environnement sous forme d’octets avec os.environ (utile lorsque Python est unicode, mais pas le système).

Python permet également de modifier ces variables d’environnement à l’aide...

Gestion d’un fichier

1. Changer les droits d’un fichier

Cette section est toujours relative à la manipulation d’un fichier, mais il y a une différence fondamentale avec ce qui précède. Ici, on n’est plus dans des opérations d’entrée/sortie sur un fichier, mais plutôt dans des opérations sur le système de fichiers.

Il est alors évident que cette fonctionnalité est intimement liée au système d’exploitation et il y en a deux catégories. Ceux s’appuyant sur une technologie Unix, que ce soit rigoureusement identique ou en y apportant une touche personnelle (GNU/Linux, FreeBSD, et donc Mac qui s’appuie sur FreeBSD) et le reste du monde, c’est-à-dire Windows.

Il est donc parfaitement logique de trouver ces fonctionnalités dans le module os. De plus, nous aurons également besoin du module stat qui renferme les différentes constantes permettant de décrire un droit sur un fichier.

>>> import os, stat 

Le système d’exploitation Windows permet de marquer un fichier en lecture seule ou en lecture et écriture.

Voici comment positionner un fichier en lecture seule :

>>> os.chmod(filepath, stat.S_IREAD) 

Voici comment le mettre en lecture et écriture :

>>> os.chmod(filepath, stat.S_IWRITE) 

Toute autre option passée ne sera simplement pas prise en compte (pas d’erreurs).

Pour Unix, la liste des options est plus conséquente (http://docs.python.org/library/os.html#os.chmod). Ces options permettent de positionner ou retirer les droits de lecture, écriture, exécution pour l’utilisateur, le groupe et les autres. Les explications des options sont abordées dans la documentation officielle (http://docs.python.org/library/stat.html#stat.S_ISUID).

Comment retenir ces sigles barbares ? L’idée, en réalité, est d’avoir un nom de constante court. Il faut commencer par retenir R pour lecture, W pour écriture et X pour exécution. Lorsqu’une seule consigne est donnée, trois lettres désignent le « qui » et si RWX est appliqué simultanément, une seule lettre est utilisée. Il s’agit de U ou USR pour le propriétaire, G ou GRP pour le groupe et O ou OTH pour les autres.

Ensuite...

Alternatives simples à des commandes bash usuelles

1. Répertoires

Python permet de parcourir des répertoires, d’en créer, d’en supprimer et de positionner des droits. On utilise toujours le même module :

>>> import os 

Voici comment se positionner dans ce dossier (équivalent de cd) :

>>> os.chdir('test') 

Et comment savoir dans quel dossier on est positionné (équivalent de pwd) :

>>> os.getcwd() 
'/home/sch/Documents/Bouquin_Python/exemples/14/test' 

Créer un dossier est tout aussi aisé (équivalent de mkdir) :

>>> os.mkdir('rep1') 

On peut aussi créer une arborescence (équivalent de mkdir -p) :

>>> os.mkdir('rep1', 'rep2', 'rep3') 

Pour créer une arborescence de dossiers, il y a également une solution toute faite :

>>> os.makedirs(os.sep.join(['rep2', 'test', '14'])) 

Voici comment parcourir l’arborescence ainsi créée (équivalent de ls -R) :

>>> for cur, dirs, files in os.walk(os.curdir): 
...     for name in (os.path.join(cur, d) for d in dirs): 
...         print('%10d %s' % (os.path.getsize(name), name)) 
...  
         0 ./rep1 
         0 ./rep2 
         0 ./rep2/test 
         0 ./rep2/test/14 

Cette partie mérite un peu plus d’explications. La fonction os.walk renvoie un générateur qui parcourt l’arborescence des fichiers et dossiers :

>>> for cur, dirs, files in os.walk(os.curdir): 
...     print('Dans %s, il y a %d dossiers et %d fichiers' % 
(cur,len(dirs), len(files))) 
...     if len(dirs) > 0: 
...         print('\t> Dossiers %s' % os.pathsep.join(dirs))
...     if len(files) > 0: 
...         print('\t> Fichiers %s' % os.pathsep.join(files))
...  
Dans ., il y a 2 dossiers et 0 fichiers 
   > Dossiers rep1:rep2 
Dans ./rep1, il y a 0 dossiers et 0 fichiers 
Dans...

Exécuter des commandes externes

1. Exécuter et afficher le résultat

Python propose plusieurs moyens de réaliser des appels à des commandes systèmes et de récupérer le résultat.

Le module os comporte des outils de bas niveau très pointus, mais destinés à ceux qui connaissent déjà les grands principes de la programmation système.

Pour les besoins de ce chapitre, nous voulons être capables de lancer dans la console Python des commandes que l’on pourrait lancer dans un terminal.

Le module utilisé est le suivant :

>>> import subprocess 

Voici comment procéder :

>>> retcode = subprocess.call('ls') 
data.txt  test.txt 

Le résultat de la commande est affiché à l’écran et le code de retour est récupéré au retour de la fonction call :

>>> print(retcode) 
0 

Tout programme qui se termine correctement renvoie un 0, sinon il renvoie un chiffre qui correspond à un code d’erreur.

Lorsque l’on veut passer des paramètres, il faut alors différencier la commande et les paramètres :

>>> retcode = subprocess.call(['ls', '-l']) 
total 128 
-rw-r--r-- 1 sch sch 35099 2011-10-22 17:56 data.txt 
-rw-r--r-- 1 sch sch     0 2011-10-22 17:38 test.txt 

Les paramètres doivent être...

IHM système

1. Présentation

En programmation système, l’application interagit principalement avec le système. Elle va donc utiliser les outils du système, ce qui est vrai en particulier pour l’interface homme-machine, c’est-à-dire pour la manière de donner de l’information à l’utilisateur et de lui en demander.

Dans ce contexte, l’utilisateur a accès à un terminal. La première chose qu’il va faire est de lancer l’application. Ceci permet de passer des arguments à l’application, comme le montrent ces exemples classiques :

$ top 
$ cp fichier1 fichier 2 
$ cp -t directory fichier1 fichier2 fichier3 

En Python, ces arguments sont disponibles dans sys.argv et le premier d’entre eux est le nom de l’exécutable si l’on est en console ou celui du fichier exécuté. On peut le visualiser en écrivant ceci dans un fichier :

import sys 
for arg in sys.argv: 
    print(arg) 

Et voici comment on exécute ce programme :

$ python3 argv.py -t dest f1 f2 f3 --test 
argv.py 
-t 
dest 
f1 
f2 
f3 
--test 

La gestion manuelle de ces arguments pourrait s’avérer extrêmement compliquée sans l’aide du module de haut niveau argparse. Ce module permet également de générer une documentation, gérer les erreurs et éventuellement une logique complexe derrière des arguments.

Une fois que l’application se termine, alors elle doit renvoyer un nombre entier positif : soit 0 si elle s’est terminée correctement, soit un nombre strictement positif. La valeur de ce nombre n’a pas d’importance pour le système, mais vous pouvez lui donner une signification en attribuant une valeur à une erreur spécifique.

Pour quitter un programme Python, soit vous laissez s’exécuter la toute dernière ligne du programme et il rend la main avec 0, soit vous utilisez sys.exit(42) par exemple.

Enfin, pendant l’utilisation du programme, vous pourrez communiquer avec l’utilisateur via les trois canaux de communication que le système met en place : l’entrée standard, la sortie standard et la sortie d’erreur.

2. Travailler avec des arguments

Habituellement, lorsqu’on lance...

Curses

Ce chapitre cible en particulier les développeurs qui sont déjà à l’aise avec Python et habitués à la programmation système.

1. Présentation

La bibliothèque Curses permet le développement d’interfaces homme-machine textuelles avancées à haut niveau.

Elle se charge donc de prendre en compte les particularités de chaque terminal (Linux, Unix, Xterm ou rxvt) et offre toutes les fonctionnalités nécessaires pour contrôler finement mais simplement les interactions entre la machine et l’utilisateur.

Comme la plupart des bibliothèques graphiques (Gtk, Qt, wxWidgets) et Pygame, Curses se charge d’initialiser l’écran, de réaliser l’affichage et offre une boucle événementielle permettant d’écouter les actions de l’utilisateur.

Nous réaliserons ici une présentation rapide du module, en montrant comment faire les premiers pas avec cette bibliothèque.

2. Gérer le démarrage et l’arrêt

Le terminal fonctionne de manière classique. Il affiche ce que l’utilisateur saisit et réagit à ce qu’il demande. Lorsque Curses démarre, le terminal change de comportement pour offrir non plus quelque chose de linéaire, mais une interface prenant tout l’écran.

De même, lorsque l’application utilisant Curses se termine (normalement ou par une erreur), le terminal doit être restauré.

C’est ce que fait pour nous curses.wrapper.wrapper.

curses.wrapper.wrapper(fonction_application, arguments, ...) 

La fonction application a pour premier argument...

Accès aux périphériques

Ce chapitre cible en particulier les développeurs qui sont déjà à l’aise avec Python, habitués à la programmation système et qui ont déjà des connaissances sur D-Bus.

1. Introduction

Il existe de nombreux moyens d’accéder aux périphériques, dont beaucoup sont à l’heure actuelle dépréciés. Aujourd’hui, les noyaux Linux 2.6 utilisent le système de communication inter-processus nommé D-Bus (http://www.freedesktop.org/wiki/Software/dbus), qui est actif avec KDE4, GNOME, Enlightenment, Maemo ou encore Androïd. Son principal développeur est Red Hat, dans le cadre du projet freedesktop.org. Il en existe un portage sous Windows.

Pour accéder aux périphériques, il est nécessaire de faire des opérations de bas niveau et de connaître l’API du projet (http://dbus.freedesktop.org/doc/api/html/index.html). Les principaux objets sont DBusConnection et DBusMessage.

Une documentation générale est également disponible sur le site de freedesktop.org (http://dbus.freedesktop.org/doc/dbus-python/doc/tutorial.html).

Le langage de programmation Python dispose d’un module qui permet d’utiliser D-Bus à bas niveau. Le développeur qui souhaite le faire devra donc bien connaître D-Bus ou avoir une documentation sous la main.

Dans cette partie, nous allons voir quelques cas concrets d’utilisation qui seront présentés comme des recettes à appliquer et à améliorer selon nos besoins, mais nous ne détaillerons pas le fonctionnement du module, qui relève plus de la connaissance de D-Bus que de Python.

2. Voir les partitions

Le langage Python permet d’utiliser D-Bus à bas niveau pour accéder à des informations système. Cette première recette va montrer à quel point l’important est de connaître l’API du projet D-Bus pour s’en sortir, puisque la partie Python reste relativement accessible.

Voici donc une recette qui donne des informations sur les partitions présentes sur la machine hôte du système :

import dbus 
 
bus = dbus.SystemBus() 
proxy = bus.get_object( 
    "org.freedesktop.Udisks", ...