J'utilise le module de sous processus pour démarrer un sous-processus et connecter à son flux de sortie (stdout). Je veux pouvoir exécuter des lectures non bloquantes sur sa sortie standard. Existe-t-il un moyen de rendre .readline non bloquant ou de vérifier s'il y a des données sur le flux avant d'invoquer .readline
? Je voudrais que ce soit portable ou au moins fonctionner sous Windows et Linux.
voici comment je le fais pour l'instant (il bloque .readline
si aucune donnée n'est disponible):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Mathieu Pagé
la source
la source
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Réponses:
fcntl
,select
,asyncproc
Ne sera pas utile dans ce cas.Un moyen fiable de lire un flux sans blocage quel que soit le système d'exploitation consiste à utiliser
Queue.get_nowait()
:la source
out.readline
bloquer le thread et le thread principal, et je dois attendre que readline revienne avant que tout le reste continue. Un moyen simple de contourner cela? (Je lis plusieurs lignes de mon processus, qui est également un autre fichier .py qui fait des bases de données et des choses)shelljob
pypi.python.org/pypi/shelljobJ'ai souvent eu un problème similaire; Les programmes Python que j'écris doivent souvent avoir la possibilité d'exécuter certaines fonctionnalités principales tout en acceptant simultanément les entrées utilisateur de la ligne de commande (stdin). Le simple fait de mettre la fonctionnalité de gestion des entrées utilisateur dans un autre thread ne résout pas le problème car il se
readline()
bloque et n'a pas de délai d'expiration. Si la fonctionnalité principale est terminée et qu'il n'est plus nécessaire d'attendre une entrée utilisateur supplémentaire, je veux généralement que mon programme se termine, mais il ne peut pas carreadline()
il bloque toujours dans l'autre thread en attente d'une ligne. Une solution que j'ai trouvée à ce problème est de faire de stdin un fichier non bloquant en utilisant le module fcntl:À mon avis, c'est un peu plus propre que d'utiliser les modules de sélection ou de signal pour résoudre ce problème, mais là encore, cela ne fonctionne que sous UNIX ...
la source
buffer_size
défini?Python 3.4 introduit une nouvelle API provisoire pour le
asyncio
module d' E / S asynchrone .L'approche est similaire à la
twisted
réponse basée sur @Bryan Ward - définissez un protocole et ses méthodes sont appelées dès que les données sont prêtes:Voir "Sous-processus" dans la documentation .
Il existe une interface de haut niveau
asyncio.create_subprocess_exec()
qui renvoie desProcess
objets qui permet de lire une ligne de manière asynchrone en utilisantStreamReader.readline()
coroutine (avec la syntaxeasync
/await
Python 3.5+ ):readline_and_kill()
effectue les tâches suivantes:Chaque étape peut être limitée par des secondes d'expiration si nécessaire.
la source
print(text, flush=True)
afin que le texte imprimé soit immédiatement disponible pour l'appelantreadline
. Lorsque je l'ai testé avec l'exécutable basé sur Fortran que je veux en fait emballer / surveiller, il ne met pas en mémoire tampon sa sortie, donc il se comporte comme prévu.readline_and_kill
, dans votre deuxième script, fonctionne de manière très similairesubprocess.comunicate
à la fin du processus après une opération de lecture / écriture. Je vois également que vous utilisez un seul tuyaustdout
, que le sous-processus gère comme non bloquant. J'essaie d'utiliser les deuxstdout
etstderr
je trouve que je finis par bloquer .Essayez le module asyncproc . Par exemple:
Le module s'occupe de tout le filetage comme suggéré par S.Lott.
la source
Vous pouvez le faire très facilement dans Twisted . En fonction de votre base de code existante, cela peut ne pas être aussi simple à utiliser, mais si vous créez une application tordue, des choses comme celle-ci deviennent presque triviales. Vous créez une
ProcessProtocol
classe et remplacez laoutReceived()
méthode. Twisted (selon le réacteur utilisé) n'est généralement qu'une grosseselect()
boucle avec des rappels installés pour gérer les données de différents descripteurs de fichiers (souvent des sockets réseau). LaoutReceived()
méthode consiste donc simplement à installer un rappel pour gérer les données provenant deSTDOUT
. Un exemple simple illustrant ce comportement est le suivant:La documentation Twisted contient de bonnes informations à ce sujet.
Si vous construisez l'intégralité de votre application autour de Twisted, cela rend la communication asynchrone avec d'autres processus, locaux ou distants, vraiment élégante comme celle-ci. D'un autre côté, si votre programme n'est pas construit sur Twisted, cela ne sera pas vraiment utile. Espérons que cela puisse être utile à d'autres lecteurs, même si cela ne s'applique pas à votre application particulière.
la source
select
ne devrait pas fonctionner sur les fenêtres avec des descripteurs de fichiers, selon les documentsselect()
il fait référence soit le même que vous. Je suppose cela parce queTwisted
fonctionne sur Windows ...asyncio
stdlib .select()
ci est le plus portable sur unix et unix-like, mais il y a aussi deux réacteurs disponibles pour Windows: twistedmatrix.com/documents/current/core/howto/…Utilisez select & read (1).
Pour readline () - comme:
la source
select
ne devrait pas fonctionner sur les fenêtres avec des descripteurs de fichiers, selon les documentsproc.stdout.read()
peu importe la taille de l'argument un appel bloquant.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Une solution consiste à créer un autre processus pour effectuer votre lecture du processus, ou à créer un thread du processus avec un délai d'expiration.
Voici la version filetée d'une fonction de temporisation:
http://code.activestate.com/recipes/473878/
Cependant, avez-vous besoin de lire la sortie standard à mesure qu'elle arrive? Une autre solution peut être de vider la sortie dans un fichier et d'attendre que le processus se termine en utilisant p.wait () .
la source
Avertissement: cela ne fonctionne que pour la tornade
Pour ce faire, définissez le fd sur non bloquant, puis utilisez ioloop pour enregistrer les rappels. J'ai emballé cela dans un œuf appelé tornado_subprocess et vous pouvez l'installer via PyPI:
vous pouvez maintenant faire quelque chose comme ceci:
vous pouvez également l'utiliser avec un RequestHandler
la source
threading.Thread
pour créer de nouveaux processus non bloquants? Je l'ai utilisé dans l'on_message
instance Websocket de Tornado, et cela a bien fonctionné.select
, avec les descripteurs de fichiers, ne fonctionne pas )select
appel. Je n'ai pas essayé cela sous Windows, mais vous auriez probablement des problèmes car la bibliothèque utilise lefcntl
module. Donc en bref: non cela ne fonctionnera probablement pas sous Windows.Les solutions existantes n'ont pas fonctionné pour moi (détails ci-dessous). Ce qui a finalement fonctionné était d'implémenter readline en utilisant read (1) (basé sur cette réponse ). Ce dernier ne bloque pas:
Pourquoi les solutions existantes n'ont pas fonctionné:
la source
q.get_nowait()
de ma réponse ne doit pas bloquer, jamais, c'est le point de l'utiliser. 2. Le thread qui exécute readline (enqueue_output()
fonction ) se termine par exemple sur EOF, y compris le cas où le processus de production de sortie est tué. Si vous croyez que ce n'est pas le cas; veuillez fournir un exemple de code minimal complet qui indique le contraire (peut-être comme une nouvelle question ).dcmpid = myprocess
.Voici mon code, utilisé pour intercepter chaque sortie du sous-processus ASAP, y compris les lignes partielles. Il pompe en même temps et stdout et stderr dans un ordre presque correct.
Testé et correctement travaillé sur Python 2.7 Linux et Windows.
la source
J'ajoute ce problème pour lire certains sous-processus. Ouvrir la sortie standard. Voici ma solution de lecture non bloquante:
la source
msvcrt.kbhit()
placeCette version de lecture non bloquante ne nécessite pas de modules spéciaux et fonctionnera prête à l'emploi sur la majorité des distributions Linux.
la source
Voici une solution simple basée sur des threads qui:
select
).stdout
et destderr
manière asynchrone.asyncio
(ce qui peut entrer en conflit avec d'autres bibliothèques).printer.py
reader.py
la source
L'ajout de cette réponse ici car il offre la possibilité de définir des canaux non bloquants sur Windows et Unix.
Tous les
ctypes
détails sont grâce à la réponse de @ techtonik .Il existe une version légèrement modifiée à utiliser à la fois sur les systèmes Unix et Windows.
De cette façon, vous pouvez utiliser la même fonction et exception pour le code Unix et Windows.
Pour éviter de lire des données incomplètes, j'ai fini par écrire mon propre générateur de ligne de lecture (qui renvoie la chaîne d'octets pour chaque ligne).
C'est un générateur pour que vous puissiez par exemple ...
la source
readline()
cela ne fonctionne pas avec les canaux non bloquants (tels que set usingfcntl
) sur Python 2 - pensez-vous que ce n'est plus correct? (ma réponse contient le lien (fcntl
) qui fournit les mêmes informations mais il semble supprimé maintenant). (2) Voir comment lesmultiprocessing.connection.Pipe
utilisationsSetNamedPipeHandleState
J'ai le problème de l'interrogateur d'origine, mais je ne souhaite pas invoquer de threads. J'ai mélangé la solution de Jesse avec une lecture directe () du tube et mon propre gestionnaire de tampons pour les lectures de ligne (cependant, mon sous-processus - ping - écrivait toujours des lignes complètes <une taille de page système). J'évite l'attente occupée en lisant uniquement dans une montre io enregistrée sur un objet. Ces jours-ci, j'exécute généralement du code dans un gobject MainLoop pour éviter les threads.
L'observateur est
Et le programme principal configure un ping puis appelle la boucle de messagerie gobject.
Tout autre travail est attaché aux rappels dans gobject.
la source
Les choses vont beaucoup mieux en Python moderne.
Voici un programme enfant simple, "hello.py":
Et un programme pour interagir avec lui:
Cela imprime:
Notez que le modèle réel, qui se trouve également dans presque toutes les réponses précédentes, ici et dans les questions connexes, consiste à définir le descripteur de fichier stdout de l'enfant sur non bloquant, puis à l'interroger dans une sorte de boucle de sélection. De nos jours, bien sûr, cette boucle est fournie par asyncio.
la source
La sélection module de vous aide à déterminer où se trouve la prochaine entrée utile.
Cependant, vous êtes presque toujours plus heureux avec des threads séparés. On fait un blocage lire la stdin, un autre fait où que vous soyez, vous ne voulez pas bloqué.
la source
pourquoi déranger thread & queue? contrairement à readline (), BufferedReader.read1 () ne bloquera pas l'attente de \ r \ n, il retourne ASAP s'il y a une sortie qui arrive.
la source
read1
bloquera si le premier bloc de lecture sous-jacent se produit lorsque le canal est toujours ouvert mais qu'aucune entrée n'est disponible.Dans mon cas, j'avais besoin d'un module de journalisation qui capture la sortie des applications d'arrière-plan et l'augmente (en ajoutant des horodatages, des couleurs, etc.).
Je me suis retrouvé avec un thread d'arrière-plan qui fait les E / S réelles. Le code suivant est uniquement pour les plates-formes POSIX. J'ai enlevé les pièces non essentielles.
Si quelqu'un va utiliser cette bête pendant de longues périodes, envisagez de gérer des descripteurs ouverts. Dans mon cas, ce n'était pas un gros problème.
la source
Mon problème est un peu différent car je voulais collecter à la fois stdout et stderr à partir d'un processus en cours d'exécution, mais finalement le même puisque je voulais rendre la sortie dans un widget comme son généré.
Je ne voulais pas recourir à la plupart des solutions de contournement proposées en utilisant des files d'attente ou des threads supplémentaires car ils ne devraient pas être nécessaires pour effectuer une tâche aussi courante que l'exécution d'un autre script et la collecte de sa sortie.
Après avoir lu les solutions proposées et les documents python, j'ai résolu mon problème avec l'implémentation ci-dessous. Oui, cela ne fonctionne que pour POSIX car j'utilise l'
select
appel de fonction.Je suis d'accord que les documents sont déroutants et l'implémentation est maladroite pour une telle tâche de script courante. Je crois que les anciennes versions de python ont des valeurs par défaut
Popen
et des explications différentes, ce qui a créé beaucoup de confusion. Cela semble bien fonctionner pour Python 2.7.12 et 3.5.2.La clé était de définir la
bufsize=1
mise en mémoire tampon des lignes, puisuniversal_newlines=True
de traiter comme un fichier texte au lieu d'un binaire qui semble devenir la valeur par défaut lors de la configurationbufsize=1
.ERROR, DEBUG et VERBOSE sont simplement des macros qui impriment la sortie sur le terminal.
Cette solution est IMHO efficace à 99,99% car elle utilise toujours la
readline
fonction de blocage , nous supposons donc que le sous-processus est agréable et génère des lignes complètes.Je me réjouis des commentaires pour améliorer la solution car je suis encore nouveau sur Python.
la source
J'ai créé une bibliothèque basée sur la solution de JF Sebastian . Tu peux l'utiliser.
https://github.com/cenkalti/what
la source
À partir de la réponse de JF Sebastian et de plusieurs autres sources, j'ai mis en place un simple gestionnaire de sous-processus. Il fournit la lecture non bloquante de la requête, ainsi que l'exécution de plusieurs processus en parallèle. Il n'utilise aucun appel spécifique au système d'exploitation (que je sache) et devrait donc fonctionner n'importe où.
Il est disponible sur pypi, donc juste
pip install shelljob
. Reportez-vous à la page du projet pour des exemples et des documents complets.la source
EDIT: Cette implémentation bloque toujours. Utilisez plutôt la réponse de JFSebastian .
J'ai essayé la meilleure réponse , mais le risque supplémentaire et la maintenance du code de thread étaient inquiétants.En parcourant le module io (et étant limité à 2.6), j'ai trouvé BufferedReader. Ceci est ma solution sans fil et non bloquante.la source
for line in iter(p.stdout.readline, ""): # do stuff with the line
? Il est sans thread (thread unique) et bloque lorsque votre code bloque.Je suis récemment tombé sur le même problème que je dois lire une ligne à la fois à partir du flux (exécution en queue dans le sous-processus) en mode non bloquant, je voulais éviter les problèmes suivants: ne pas graver cpu, ne pas lire le flux d'un octet ( comme readline), etc.
Voici mon implémentation https://gist.github.com/grubberr/5501e1a9760c3eab5e0a il ne prend pas en charge les fenêtres (sondage), ne gère pas EOF, mais cela fonctionne bien pour moi
la source
timeout
comme dans votre solution) et.readline()
lit plus d'un octet à la fois (bufsize=1
moyen ligne -buffered (uniquement pour l' écriture)). Quels autres problèmes avez-vous trouvés? Les réponses en lien uniquement ne sont pas très utiles.Ceci est un exemple pour exécuter une commande interactive dans un sous-processus, et la sortie standard est interactive à l'aide d'un pseudo-terminal. Vous pouvez vous référer à: https://stackoverflow.com/a/43012138/3555925
la source
Cette solution utilise le
select
module pour «lire toutes les données disponibles» à partir d'un flux d'E / S. Cette fonction se bloque initialement jusqu'à ce que les données soient disponibles, mais ne lit ensuite que les données disponibles et ne bloque plus.Étant donné qu'il utilise le
select
module, cela ne fonctionne que sur Unix.Le code est entièrement conforme à PEP8.
la source
J'ai également fait face au problème décrit par Jesse et l' ai résolu en utilisant "select" comme Bradley , Andy et d'autres l'ont fait, mais en mode de blocage pour éviter une boucle occupée. Il utilise un tuyau factice comme un faux stdin. Sélectionnez les blocs et attendez que stdin ou le tuyau soit prêt. Lorsqu'une touche est enfoncée, stdin débloque la sélection et la valeur de la clé peut être récupérée avec read (1). Lorsqu'un thread différent écrit sur le tuyau, le tuyau débloque le sélecteur et peut être considéré comme une indication que le besoin de stdin est terminé. Voici un code de référence:
la source
Essayez wexpect , qui est l'alternative Windows de pexpect .
la source
Sur les systèmes de type Unix et Python 3.5+, il y a
os.set_blocking
ce qui fait exactement ce qu'il dit.Cela produit:
Avec
os.set_blocking
commenté c'est:la source
Voici un module qui prend en charge les lectures non bloquantes et les écritures d'arrière-plan en python:
https://pypi.python.org/pypi/python-nonblock
Fournit une fonction,
nonblock_read qui lira les données du flux, si disponible, sinon retourne une chaîne vide (ou None si le flux est fermé de l'autre côté et que toutes les données possibles ont été lues)
Vous pouvez également envisager le module python-subprocess2,
https://pypi.python.org/pypi/python-subprocess2
ce qui ajoute au module de sous-processus. Ainsi, sur l'objet renvoyé par "subprocess.Popen" est ajoutée une méthode supplémentaire, runInBackground. Cela démarre un thread et retourne un objet qui sera automatiquement rempli au fur et à mesure que les trucs sont écrits dans stdout / stderr, sans bloquer votre thread principal.
Prendre plaisir!
la source