Sous-processus Python perdant 10% de la sortie stdout d'un programme

J'ai un programme qui doit être appelé en sous-processus avec python. Le programme a été écrit en Java. Ouais je sais...

De toute façon, j'ai besoin de capturer toutes les sorties de ce programme.

Malheureusement, quand j'appelle subprocess.popen2 ou subprocess.Popen avec communiquez [0], je perds environ 10% des données de sortie quand j'utilise un sous-processus.PIPE affecté à stdout ET quand j'utilise un descripteur de fichier (le retour d'un ouvert) affecté à stdout.

La documentation dans subprocess est assez explicite que l'utilisation de subprocess.PIPE est volatile si vous essayez de capturer toutes les sorties d'un processus enfant.

J'utilise actuellement pexpect pour exporter la sortie dans un fichier tmp mais cela prend une éternité pour des raisons évidentes.

Je voudrais garder toutes les données en mémoire pour éviter les écritures sur disque.

toutes les recommandations sont les bienvenues! Merci!

import subprocess

cmd = 'java -Xmx2048m -cp "/home/usr/javalibs/class:/home/usr/javalibs/libs/dependency.jar" --data data --input input" 

# doesn't get all the data
#
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]

OR
# doesn't get all the data
#
fd = open("outputfile",'w')
p = subprocess.Popen(cmd, stdout=fd, shell=True)
p.communicate()
fd.close() # tried to use fd.flush() too.

# also tried
# p.wait() instead of p.communicate(), but wait doesn't really wait for the java program to finish running - it doesn't block

OR
# also fails to get all the data
#
import popen2
(rstdout, rstdin) = popen2.popen2(cmd)

La sortie attendue est une série de lignes ASCII (quelques milliers). les lignes contiennent un nombre et un caractère de fin de ligne

0\n
1\n
4\n
0\n
...
2
Pouvons-nous avoir du code Python?
ajouté l'auteur Bittrance, source
hé, juste essayer de capturer stdout (pas stderr). la sortie est un nombre et un caractère de fin de ligne - il attend toutes les sorties ascii
ajouté l'auteur ct_, source
@ jadkik94 et le Paul donc les gars j'apprécie votre temps, mais vous n'aidez pas vraiment. J'ai déjà déclaré que PIPE a des problèmes lors de l'appel de sous-processus (j'ai lu la documentation plusieurs fois) alors comment faites-vous cela correctement?
ajouté l'auteur ct_, source
@the paul. il ne lit pas toute la sortie. J'attends ~ 1300 + lignes de chiffres avec de nouveaux caractères de ligne - en fonction des entrées. et oui l'attente n'attend pas vraiment que mon script Python continue à s'exécuter passé où je déborde le sous-processus. quant à l'exactitude, j'explique le problème que j'ai.
ajouté l'auteur ct_, source
@the paul pas tout à fait ce que je cherche en guise de réponse. si c'est volatile, où puis-je lire sur la façon de le faire correctement. et quand j'utilise le fd je ne reçois toujours pas toutes les données - je suppose que l'assignation d'un fd à stdout a les mêmes problèmes que l'assignation de subprocess.PIPE à stdout.
ajouté l'auteur ct_, source
les derniers 10% de la production. posté une mise à jour à la question pour clarifier. Merci!
ajouté l'auteur ct_, source
lequel "10%" vous manque? Est-ce au début, à la fin? Quel résultat attendiez-vous?
ajouté l'auteur Joel Cornett, source
"mais wait n'attend pas vraiment que le programme java finisse de tourner - il ne bloque pas" <- aussi complètement inexact. Êtes-vous sûr que votre sous-processus fonctionne comme vous le souhaitez?
ajouté l'auteur the paul, source
Quelles erreurs obtenez-vous?
ajouté l'auteur the paul, source
À droite, subprocess.PIPE doit être utilisé avec communicate() ou avec prudence, pour éviter que les fds d'entrée et de sortie ne se bloquent les uns les autres. C'est en partie ce que je voulais dire par "correctement utilisé".
ajouté l'auteur the paul, source
"La documentation dans subprocess est assez explicite que l'utilisation de subprocess.PIPE est volatile si vous essayez de capturer toutes les sorties d'un processus enfant." <- si la documentation le dit, c'est complètement faux. PIPE est parfaitement sûr et obtiendra toutes les sorties sur le fd connecté s'il est correctement utilisé.
ajouté l'auteur the paul, source
Etes-vous sûr que votre sous-processus Java n'est pas lui-même forking? Cela peut expliquer pourquoi votre appel wait() ne semble pas bloquer.
ajouté l'auteur the paul, source
Je vous assure, PIPE n'a pas de "problèmes" si vous utilisez communiquez et que vos données tiennent dans la mémoire (si ce n'est pas le cas, vous verrez un échec beaucoup plus évident). Les notes dans les docs sont d'empêcher les gens d'essayer de l'utiliser d'une manière inappropriée. Je voudrais aider, mais il semble que vous voulez vraiment blâmer le mauvais côté du système.
ajouté l'auteur the paul, source
Pour être plus précis, utiliser subprocess.PIPE ou assigner un fd à la sortie du sous-processus est essentiellement la même chose que votre shell quand vous faites une redirection vers un fichier (le code du système d'exploitation ( ) appel système). Vous pouvez supposer que cette partie fonctionne. Vous pouvez essayer d'ajouter "` teecopy "à la fin de votre commande; alors vous pouvez vérifier que outputcopy` a toutes les lignes que vous attendez. Si ce n'est pas le cas, peut-être que votre programme Java ne fonctionne pas correctement.
ajouté l'auteur the paul, source
@ jadkik94 qui est très peu susceptible d'être un problème ici; "quelques milliers" lignes de quelques caractères pourraient facilement entrer dans la mémoire, sur n'importe quelle machine imaginable capable d'exécuter Python ou Java du tout.
ajouté l'auteur the paul, source
Est-il possible qu'une partie de la sortie soit écrite dans stderr?
ajouté l'auteur Jeremiah, source
Remarque: N'utilisez pas stdout = PIPE ou stderr = PIPE avec cette fonction: comme les canaux ne sont pas lus dans le processus en cours, le processus enfant peut bloquer s'il génère suffisamment de sortie vers un canal pour remplir le tampon du tube OS. à partir de documentation de sous-processus
ajouté l'auteur jadkik94, source
Il y a un avertissement dans communiquer sur des données volumineuses, mais il reste est très peu clair pour une alternative ...
ajouté l'auteur jadkik94, source
Voir si cela peut aider aussi: une autre question SO
ajouté l'auteur jadkik94, source

2 Réponses

Cela doit être lié au processus que vous appelez. Vous pouvez le vérifier en effectuant un test simple avec un autre script python qui renvoie des lignes:

out.py

import sys

for i in xrange(5000):
    print "%d\n" % i

sys.exit(0)

test.py

import subprocess

cmd = "python out.py"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
output = p.communicate()[0]

print output

Vous pouvez donc vérifier que ce n'est pas la taille des données qui pose problème, mais plutôt la communication avec le processus que vous appelez.

Vous devriez également confirmer la version de python que vous utilisez, comme je l'ai lu à propos de problèmes antérieurs concernant le tampon interne de Popen (mais en utilisant un handle de fichier séparé comme vous l'avez suggéré normalement fixé pour moi).

Ce serait un problème de tampon si l'appel de sous-processus était suspendu indéfiniment. Mais si le processus se termine, il manque juste des lignes, alors Popen fait son travail.

2
ajouté
Je vais faire un tour dans un peu, et posterai des résultats, merci!
ajouté l'auteur ct_, source

J'avais utilisé subprocess avec une sortie beaucoup plus grande sur stdout mais je n'ai pas vu un tel problème. Il est difficile de conclure quelle est la cause première de ce que vous avez montré. Je vérifierais:

Puisque p.wait() n'a pas fonctionné pour vous. Il se peut que lorsque vous lisez votre PIPE , votre programme Java soit encore occupé à imprimer les 10% restants. Obtenez d'abord p.wait() :

  • Insérez une attente suffisamment longue (disons 30 secondes) avant de lire PIPE , est-ce que votre 10% apparaît?
  • Il est douteux que p.wait() ne bloque pas votre programme java. Est-ce que votre programme Java poursuit le sous-traitement d'autres programmes?
  • vérifiez la valeur de retour de p.wait() . Votre programme Java s'est-il terminé normalement?

Si le problème ne réside pas dans votre modèle de concurrence, vérifiez si vous imprimez correctement dans votre programme Java:

  • Quelle fonction avez-vous utilisée dans votre programme java pour imprimer stdout ? Est-il sujet à ou ignore-t-il IOException ?
  • Avez-vous vidé le flux correctement? Le dernier 10% pourrait être dans votre tampon sans vidage correct lorsque votre programme Java se termine.
2
ajouté
reviendra avec vous - aller travailler sur les notes de jdi dans un peu. Merci!
ajouté l'auteur ct_, source