L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.
Python 3 comprend le module subprocess
permettant d’exécuter des programmes externes et de lire leurs sorties dans votre code Python.
Il se peut que vous trouviez subprocess
utile si vous voulez utiliser un autre programme sur votre ordinateur à partir de votre code Python. Par exemple, vous pouvez invoquer git
depuis votre code Python pour récupérer les fichiers de votre projet qui sont suivis dans le contrôle de version de git
. Comme tout programme auquel vous pouvez accéder sur votre ordinateur par subprocess
, les exemples présentés ici s’appliquent à tout programme externe que vous pourriez vouloir invoquer à partir de votre code Python.
subprocess
comprend plusieurs classes et fonctions, mais dans ce tutoriel, nous couvrirons l’une des fonctions les plus utiles de subprocess
: subprocess.run
. Nous passerons en revue ses différentes utilisations et les principaux arguments des mots-clés.
Pour tirer le meilleur parti de ce tutoriel, il est recommandé d’être familiarisé avec la programmation en Python 3. Vous pouvez consulter ces tutoriels pour obtenir les informations de base nécessaires :
Vous pouvez utiliser la fonction subprocess.run
pour exécuter un programme externe à partir de votre code Python. Mais d’abord, vous devez importer les modules subprocess
et sys
dans votre programme :
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "print('ocean')"])
Si vous l’exécutez, vous obtiendrez une sortie comme ci-dessous :
Outputocean
Passons en revue cet exemple :
sys.executable
est le chemin absolu vers l’exécutable Python avec lequel votre programme a été invoqué à l’origine. Par exemple, sys.executable
pourrait être un chemin tel que /usr/local/bin/pyth
on.subprocess.run
reçoit une liste de chaînes de caractères comprenant les composants de la commande que nous essayons d’exécuter. Comme la première chaîne que nous passons est sys.executable
, nous ordonnons à subprocess.run
d’exécuter un nouveau programme Python.-c
est une option de ligne de commande python
qui vous permet de passer une chaîne avec un programme Python entier à exécuter. Dans notre cas, nous passons un programme qui imprime la chaîne ocean
.Vous pouvez penser que chaque entrée de la liste que nous passons à subprocess.run
est séparée par un espace. Par exemple, [sys.executable, "-c", "print('ocean')"]
se traduit approximativement par /usr/local/bin/python -c "print('ocean')"
. Notez que subprocess
cite automatiquement les composants de la commande avant d’essayer de les exécuter sur le système d’exploitation sous-jacent de sorte que, par exemple, vous pouvez passer un nom de fichier qui contient des espaces.
Warning : ne jamais transmettre une entrée non fiable à subprocess.run
. Comme subprocess.run
a la capacité d’exécuter des commandes arbitraires sur votre ordinateur, des acteurs malveillants peuvent l’utiliser pour manipuler votre ordinateur de manière inattendue.
Maintenant que nous pouvons invoquer un programme externe en utilisant subprocess.run
, voyons comment nous pouvons récupérer la sortie de ce programme. Par exemple, ce processus pourrait être utile si nous voulions utiliser git ls-files
pour produire tous vos fichiers actuellement stockés sous contrôle de version.
Note : Les exemples présentés dans cette section nécessitent Python 3.7 ou version supérieure. En particulier, les arguments capture_output
et le mot-clé text
ont été ajoutés à Python 3.7 lors de sa sortie en juin 2018.
Ajoutons à notre exemple précédent :
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('ocean')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
Si nous exécutons ce code, nous obtiendrons le résultat suivant :
Outputstdout: ocean
stderr:
Cet exemple est en grande partie le même que celui présenté dans la première section : nous sommes toujours en train d’exécuter un sous-processus pour imprimer ocean
. Il est toutefois important de noter que nous passons les arguments des mots-clés capture_output=True
et text=True
à subprocess.run
.
subprocess.run
renvoie un objet subprocess.CompletedProcess
qui est lié à result
. L’objet subprocess.CompletedProcess
comprend des détails sur le code de sortie du programme externe et sa sortie. capture_output=True
garantit que result.stdout
et result.stderr
sont remplis avec la sortie correspondante du programme externe. Par défaut, result.stdout
et result.stderr
sont liés en tant qu’octets, mais l’argument du mot-clé text=True
indique à Python de décoder plutôt les octets en chaînes de caractères.
Dans la section de sortie, stdout
est ocean
(plus la nouvelle ligne de fin que print
ajoute implicitement), et nous n’avons pas de stderr
.
Essayons un exemple qui produit une valeur non vide pour stderr
:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "raise ValueError('oops')"], capture_output=True, text=True
)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
Si nous exécutons ce code, nous obtenons une sortie comme celle qui suit :
Outputstdout:
stderr: Traceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Ce code exécute un sous-processus Python qui génère immédiatement une ValueError
. Lorsque nous inspectons le result
final, nous ne voyons rien dans stdout
et un Traceback
de notre ValueError
dans stderr
. C’est parce que par défaut Python écrit le Traceback
de l’exception non gérée à stderr
.
Il est parfois utile de lever une exception si un programme que nous exécutons sort avec un code de sortie incorrect. Les programmes qui sortent avec un code nul sont considérés comme réussis, mais les programmes qui sortent avec un code non-nul sont considérés comme ayant rencontré une erreur. Par exemple, ce modèle pourrait être utile si nous voulions lever une exception dans le cas où nous exécutons des fichiers git ls-files
dans un répertoire qui n’est pas réellement un référentiel git
.
Nous pouvons utiliser l’argument check=True
du mot-clé subprocess.run
pour qu’une exception soit levée si le programme externe renvoie un code de sortie non-nul :
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"], check=True)
Si nous exécutons ce code, nous obtenons une sortie comme celle qui suit :
OutputTraceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 512, in run
raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.
Cette sortie montre que nous avons exécuté un sous-processus qui a généré une erreur imprimée en stderr
dans notre terminal. Ensuite, subprocess.run
a consciencieusement levé un subprocess.CalledProcessError
en notre nom dans notre programme Python principal.
Alternativement, le module de sous-processus
comprend également la méthode subprocess.CompletedProcess.check_returncode
que nous pouvons invoquer pour un effet similaire :
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "raise ValueError('oops')"])
result.check_returncode()
Si nous exécutons ce code, nous recevrons :
OutputTraceback (most recent call last):
File "<string>", line 1, in <module>
ValueError: oops
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 444, in check_returncode
raise CalledProcessError(self.returncode, self.args, self.stdout,
subprocess.CalledProcessError: Command '['/usr/local/bin/python', '-c', "raise ValueError('oops')"]' returned non-zero exit status 1.
Comme nous n’avons pas passé check=True
à subprocess.run
, nous avons lié avec succès une instance de subprocess.CompletedProcess
à result
, même si notre programme s’est terminé avec un code non nul. L’appel de result.check_returncode()
fait cependant apparaître un sous-processus appelé CalledProcessError
parce qu’il détecte le processus terminé sorti avec un code incorrect.
subprocess.run
inclut l’argument timeout
pour vous permettre d’arrêter un programme externe si son exécution est trop longue :
import subprocess
import sys
result = subprocess.run([sys.executable, "-c", "import time; time.sleep(2)"], timeout=1)
Si nous exécutons ce code, nous obtiendrons le résultat suivant :
OutputTraceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python3.8/subprocess.py", line 491, in run
stdout, stderr = process.communicate(input, timeout=timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1024, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1892, in _communicate
self.wait(timeout=self._remaining_time(endtime))
File "/usr/local/lib/python3.8/subprocess.py", line 1079, in wait
return self._wait(timeout=timeout)
File "/usr/local/lib/python3.8/subprocess.py", line 1796, in _wait
raise TimeoutExpired(self.args, timeout)
subprocess.TimeoutExpired: Command '['/usr/local/bin/python', '-c', 'import time; time.sleep(2)']' timed out after 0.9997982999999522 seconds
Le sous-processus que nous avons essayé d’exécuter utilisait la fonction time.sleep
pour se mettre en veille pendant 2
secondes. Cependant, nous avons passé l’argument du mot-clé timeout=1
à subprocess.run
pour que notre sous-processus soit temporisé après 1
seconde. Cela explique pourquoi notre appel à subprocess.run
a finalement soulevé une exception de subprocess.TimeoutExpired
.
Notez que l’argument du mot-clé timeout
à subprocess.run
est approximatif. Python fera tout son possible pour arrêter le sous-processus après le nombre de secondes stipulé dans timeout
, mais ce ne sera pas nécessairement exact.
Parfois, les programmes s’attendent à ce que les données leur soient transmises via stdin
.
L’argument du mot-clé input
à subprocess.run
vous permet de passer des données au stdin
du sous-processus. Par exemple :
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], input=b"underwater"
)
Après l’exécution de ce code, nous recevrons une sortie comme celle qui suit :
Outputunderwater
Dans ce cas, nous avons passé les octets underwater
à input
. Notre sous-processus cible a utilisé sys.stdin
pour lire le passage dans stdin
( underwater
) et l’a imprimé dans notre sortie.
L’argument du mot-clé input
peut être utile si vous voulez enchaîner plusieurs appels subprocess.run
ensemble en passant la sortie d’un programme comme entrée à un autre.
Le module subprocess
est une partie puissante de la bibliothèque standard Python, qui vous permet d’exécuter des programmes externes et d’inspecter leurs sorties facilement. Dans ce tutoriel, vous avez appris à utiliser subprocess.run
pour contrôler des programmes externes, leur transmettre des entrées, analyser leurs sorties et vérifier leurs codes de retour.
Le module subprocess
propose des classes et des utilitaires supplémentaires que nous n’avons pas abordés dans ce tutoriel. Maintenant que vous disposez d’une base de référence, vous pouvez utiliser la documentation du module subprocess
pour en savoir plus sur d’autres classes et utilitaires disponibles.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!