Python : Programmation système
L'ensemble de ces notes est issu de : https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/234239-un-peu-de-programmation-systeme
Accéder aux flux standards (stin, stdout, stderr)
L'entrée standard est utilisée par input, la sortie standard est utilisée pour les exceptions (entre autres). En python, les flux standard sont utilisés avec la bibliothèque "sys"; et comme pour utiliser des fichiers, on utilise les méthodes "read" et "write". stdin peut lire et les deux autres écrire.
>>> sys.stdout.write("un test")
un test7
>>>
Ici, on écrit dans la sortie standard, et ça affiche aussi le nombre de caractères.
Lire depuis le pipe
Le moyen le plus simple de lire du texte depuis le pipe est le suivant :
content = sys.stdin.read() content = str(content)
Modifier les flux standards
On peut modifier sys.stdin, sys.stdout, et sys.stderr :
>>> fichier = open('sortie.txt', 'w')
>>> sys.stdout = fichier
>>> print("Quelque chose...")
>>>
Ici, rien ne s'affiche; cependant, "Quelque chose..." est bien écrit dans le fichier sortie.txt, qui doit se trouver dans le répertoire courant de Python (et sans le nmbre de caractères à la fin, puisqu'on a utilisé print!). On peut accéder à ce répertoire à l'aide de la commande :
import os
sys.stdout = sys.__stdout__ #Sans ça, on affiche rien : la sortie va dans un fichier
os.getcwd()
Si on a modifié les flux standards, on peut toujours les retrouver dans sys.__stdin__, sys.__stdout__, sys.__stderr__. La documentation conseille quand même de les garder sous la main plutôt que d'aller les chercher dans sys.
Les signaux
Les signaux sont utilisés par le système pour communiquer avec les programmes, par exemple pour les arrêter. On peut capturer ces signaux dans notre programme : cela peut par exemple permettre de faire certaines actions si le programme doit se fermer (enregistrer des objets dans un fichier, fermer des connexions réseau...). Les signaux permettent aussi aux programmes de communiquer entre eux, mais on ne verra pas ça ici (dommage...).
Les différents signaux
Tous les signaux en sont pas communs à tous les OS, alors on va ici surtout s'attacher à SIGINT, envoyé pour fermer un programme.
Pour plus de précisions : https://docs.python.org/3/library/signal.html
Intercepter un signal
On commence par importer le module signal :
import signal
Je peux ensuite voir mon signal avec signal.SIGINT, par exemple. Pour intercepter un signal, il faut créer une fonction qui sera appellée si le signal est envoyé. ELle prend deux paramètres :
- Le signal (on peut envoyer plusieurs signaux à une même fonction)
- le frame, dont on ne parlera pas ici.
Il faut donc créer la fonction et la connecter avec le signal SIGINT :
def fermerProgramme(signal, frame):
"""Fonction appelée quand vient l'heure de fermer notre programme"""
print("C'est l'heure de la fermeture !")
sys.exit(0)
Ici, le code de retour 0 signifie "Pas d'erreur"; n'importe quel autre entier signifierait une erreur. Le sys.exit sert juste à fermer le programme.
Ensuite, il faudra connecter le signal à cette fonction : on utilise pour ça la fonction signal du module signal. Elle prend en paramètres :
- Le signal à intercpter
- La fonction que l'on doit y connecter
Ce qui donne :
signal.signal(signal.SIGINT, fermer_programme)
On ne met pas les parenthèses après fermer_programme, puisqu'on l'utilise en tant qu'objet, mais on ne l'exécute pas.
Un code complet d'exemple
import signal #J'importe mes modules
import sys
def fermer_programme(signal, frame): #Ma fonction qui sera appellée lors du signal; elle contient bien ses deux objets
"""Fonction appelée quand vient l'heure de fermer notre programme"""
print("C'est l'heure de la fermeture !")
sys.exit(0)
# Connexion du signal à notre fonction
signal.signal(signal.SIGINT, fermer_programme)
# Notre programme...
print("Le programme va boucler...")
while True:
continue
Si je lance ce script, ma boucle va tourner à l'infini, jusqu'à ce que j'interrompe le script, auquel cas la fonction fermer_programme s'exécutera.
Les arguments
Les arguments passés dans la ligne de commande peuvent être obtenus grâce au module sys; notamment sa variable argv. sys.argv contient une liste des arguments que l'on passe à la ligne de commande, en lancant le programme. sys.argv contient une liste contenant chacun des arguments, le premier étant toujours le nom du programme lui-même.
Exemple :
Le code suivant :
import sys
print(sys.argv)
Donne ceci une fois lancé :
['signaux.py', 'argument1', 'argument2', 'Une phrase entre guillemets']
On voit ici que les arguments sont renvoyés sous la forme d'une liste de strings, un argument entre guillemets pouvant contenir des espaces.
Récupérer et utiliser des arguments
Tout ça c'est bien, mais on doit pouvoir utiliser nos arguments : un start, un stop, un restart... Un cas de figure simple donnerait ceci :
import sys
if len(sys.argv) < 2:
print("Précisez une action en paramètre")
sys.exit(1)
action = sys.argv[1]
if action == "start":
print("On démarre l'opération")
elif action == "stop":
print("On arrête l'opération")
elif action == "restart":
print("On redémarre l'opération")
elif action == "status":
print("On affiche l'état (démarré ou arrêté ?) de l'opération")
else:
print("Je ne connais pas cette action")
Mais on peut faire des choses plus complexes, comme utiliser des arguments courts ( -a) ou longs (--argument). Un argument court est souvent relié à un argument long.
argparse
On va utiliser le module argparse, utile pour interpréter les arguments selon un certain schéma.Sa base est la suivante :
import argparse #On importe le module
parser = argparse.ArgumentParser() #On crée cet objet qui servira à configurer nos options à interpréter
parser.parse_args() #On appelle cette méthode, qui servira à retourner les arguments interprétés
Nous allons voir comment préciser des options à parser, pour que ce soit plus intéressant. À noter, par défaut parser.parse_args() commence à partir de sys.argv[1], c'est-à-dire qu'il ne prend pas en compte le tout premier argument contenant le nom du script. D'ailleurs, notre parser n'est par défaut pas tout à fait vide, il contient un argument -h qui renvoie une aide. En exécutant le code montré juste avant avec -h, on a :
>python code.py --help
usage: code.py [-h]
optional arguments:
-h, --help show this help message and exit
Si j'utilise un argument qu'on a pas déclaré auparavant, il renvoie une erreur. Pour déclarer des arguments :
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("x", help="le nombre a mettre au carré")
parser.parse_args()
La méthode add_argument prend ici deux arguments (même si elle en plein d'autres en option) : l'option et le message d'aide qui y est relié. Je peux voir ce message d'aide :
>python code.py --help
usage: code.py [-h] x
positional arguments:
x le nombre à mettre au carré
optional arguments:
-h, --help show this help message and exit
J'ai désormais une optioni x, je peux alors y mettre un nombre en appelant mon script et le récupérer pour en faire quelque chose :
parser = argparse.ArgumentParser()
parser.add_argument("x", type=int, help="le nombre a mettre au carré")
args = parser.parse_args()
print("Vous avez donné en argument", args.x)
x = args.x
retour = x ** 2
print(retour)
Ici, on récupère que des nombres. J'ai ajouté un type=int, pour que l'utilisateur ne puisse rentrer que des nombres. Ce code fait finalement quelque chose de concret, à savoir : mettre un nombre au carré.
Utiliser des options facultatives
Jusque ici, on a utilisé des "positionnal arguments", qui sont précisés sans option. Cependant on peut aussi utiliser des options facultatives, du type --verbose ou -v :
import argparse
parser = argparse.ArgumentParser() #Je crée mon objet parser
parser.add_argument("x", type=int, help="le nombre à mettre au carré") #Je lui ajoute un argument positionnel
parser.add_argument("-v", "--verbose", action="store_true", help="augmente la verbosité") #Je lui ajoute une option
args = parser.parse_args() #Je parse les arguments
x = args.x #Je récupère mon argument positionnel
retour = x ** 2
if args.verbose: #J'utilise mon option
print("{} ^ 2 = {}".format(x, retour))
else:
print(retour)
Plusieurs choses ici :
- On a rajouté -v ou --verbose. Quand un nom commence par un tiret, argparse suppose que celui-ci est un argument facultatif (même si cette attitude d'argparse peut être modifiée).
- On a utilisé l'argument action, avec store_true :
- store_true permet de convertir l'option en booléen
- Si l'argument est précisé, alors args.verbose vaudra True
- Sinon, il vaudra False
Ce n'est qu'un début : sont dispos un tutoriel sur argparse (https://docs.python.org/3/howto/argparse.html) et la doc (https://docs.python.org/3/library/argparse.html)
Exécuter des commandes systèmes sous Python
Nous allons ici voir deux façons de le faire, qui ont le bon goût de marcher sous Windows.
La fonction os.system
Cette fonction du module os prend en paramètre une commande à exécuter, affiche le résultat et renvoie le code de retour; on peut récupérer le code de retour mais pas l'affichage de la commande (qui s'affiche dans le terminal). De plus, cette commande utilise un environnement particulier rien que pour la commande; du coup un os.system("sleep 5") n'arrêtera pas le programme 5 secondes.
import os cmd = "date" #Utiliser simplement la commande pour afficher la date os.system(cmd) #Récupérer la valeur retournée, ce qui exécute aussi la commande en même temps valeurRetournee = os.system(cmd) print("La commande a renvoyé :", valeurRetournee)
La fonction os.popen
Cette fonction prend aussi une commande en paramètre; cependant au lieu de renvoyer le code retour, elle renvoie un objet appellé un pipe (rien à voir avec | ), qui permet de lire ce que la commande renvoie (le texte qu'elle renvoie). Lire le pipe bloque le script jusqu'à ce que la commande ait fini de s'exécuter. D'ailleurs, le fait de lire le pipe renvoie un string, mais n'affiche rien en soit dans le terminal (il faut utiliser ce string pour l'afficher par exemple).
import os
cmd = os.popen("ls")
#cmd contient mon pipe
#Je vais lire mon pipe, et afficher le résultat, tant qu'à faire...
print(cmd.read())
La lib subprocess
https://docs.python.org/fr/3/library/subprocess.html
Subprocess permet de faire beaucooup de choses et est simple à utiliser: <source lang="python"> resultat_commande = subprocess.run(["ls", "-l"]) </source> La variable resultat_commande contiendra alors la sortie de la commande.