One place for hosting & domains

      Comment utiliser le module subprocess pour exécuter des programmes externes en Python 3


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      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.

      Conditions préalables

      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 :

      Exécution d’un programme externe

      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 :

      Output

      ocean

      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/python.
      • 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.
      • Le composant -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.

      Capturer output d’un programme externe

      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 :

      Output

      stdout: 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 :

      Output

      stdout: 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 .

      Lever une exception sur un code de sortie incorrect

      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 :

      Output

      Traceback (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 :

      Output

      Traceback (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.

      Utilisation du délai d’attente pour quitter prématurément les programmes

      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 :

      Output

      Traceback (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.

      Transmission d’Input aux programmes

      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 :

      Output

      underwater

      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.

      Conclusion

      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.



      Source link