xticks xlabel python windows subprocess sublimetext3 popen

xlabel - plot python



Proceso de lectura de stdout en tiempo real. (5)

¿Cómo se evalúa internamente la línea en p.stdout? ¿Es una especie de bucle sin fin hasta que llega a stdout eof o algo así?

p.stdout es un búfer (bloqueo). Cuando estás leyendo desde un búfer vacío , estás bloqueado hasta que algo se escribe en ese búfer. Una vez que hay algo en él, obtienes los datos y ejecutas la parte interna.

Piense en cómo funciona tail -f en linux: espera hasta que se escriba algo en el archivo y, cuando lo haga, muestra los nuevos datos en la pantalla. ¿Qué pasa cuando no hay datos? espera Entonces, cuando su programa llega a esta línea, espera los datos y los procesa.

A medida que su código funciona, pero cuando se ejecuta como modelo no, tiene que estar relacionado con esto de alguna manera. El módulo http.server probablemente almacena en búfer la salida. Intente agregar el parámetro -u a Python para ejecutar el proceso sin búfer:

-u: stdout y stderr binarios sin búfer; también PYTHONUNBUFFERED = x vea la página del manual para obtener detalles sobre el almacenamiento en búfer interno relacionado con ''-u''

Además, es posible que desee intentar cambiar su bucle a for line in iter(lambda: p.stdout.read(1), ''''): ya que esto lee 1 byte a la vez antes de procesarlo.

Actualización : el código de bucle completo es

for line in iter(lambda: p.stdout.read(1), ''''): sys.stdout.write(line) sys.stdout.flush()

Además, usted pasa su comando como una cadena. Intente pasarlo como una lista, con cada elemento en su propia ranura:

cmd = [''python'', ''-m'', ''http.server'', ..]

Consideremos este fragmento:

from subprocess import Popen, PIPE, CalledProcessError def execute(cmd): with Popen(cmd, shell=True, stdout=PIPE, bufsize=1, universal_newlines=True) as p: for line in p.stdout: print(line, end='''') if p.returncode != 0: raise CalledProcessError(p.returncode, p.args) base_cmd = [ "cmd", "/c", "d://virtual_envs//py362_32//Scripts//activate", "&&" ] cmd1 = " ".join(base_cmd + [''python -c "import sys; print(sys.version)"'']) cmd2 = " ".join(base_cmd + ["python -m http.server"])

Si ejecuto execute(cmd1) la salida se imprimirá sin problemas.

Sin embargo, si ejecuto execute(cmd2) lugar, no se imprimirá nada, ¿por qué? ¿Cómo y cómo puedo solucionarlo para poder ver el resultado de http.server en tiempo real?

Además, ¿cómo se evalúa internamente la for line in p.stdout ? ¿Es una especie de bucle sin fin hasta que llega a stdout eof o algo así?

Este tema ya se ha abordado varias veces aquí en SO, pero aún no he encontrado una solución de Windows. El fragmento de código anterior es un código de hecho de esta answer y trata de ejecutar http.server desde un virtualenv (python3.6.2-32bits en win7)


Con este código, no puede ver la salida en tiempo real debido al almacenamiento en búfer:

for line in p.stdout: print(line, end='''')

Pero si usas p.stdout.readline() debería funcionar:

while True: line = p.stdout.readline() if not line: break print(line, end='''')

Vea la discusión correspondiente sobre los errores de Python para más detalles.

UPD: aquí puede encontrar casi el mismo problema con varias soluciones en .


Creo que el principal problema es que http.server alguna manera está registrando la salida en stderr , aquí tengo un ejemplo con asyncio , leyendo los datos de stdout o stderr .

Mi primer intento fue usar asyncio, una API agradable, que existe desde Python 3.4. Más tarde, encontré una solución más simple, para que pueda elegir, ambos deberían funcionar.

asyncio como solucion

En segundo plano, asyncio está utilizando IOCP , una API de Windows para async cosas.

# inspired by https://pymotw.com/3/asyncio/subprocesses.html import asyncio import sys import time if sys.platform == ''win32'': loop = asyncio.ProactorEventLoop() asyncio.set_event_loop(loop) async def run_webserver(): buffer = bytearray() # start the webserver without buffering (-u) and stderr and stdin as the arguments print(''launching process'') proc = await asyncio.create_subprocess_exec( sys.executable, ''-u'', ''-mhttp.server'', stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) print(''process started {}''.format(proc.pid)) while 1: # wait either for stderr or stdout and loop over the results for line in asyncio.as_completed([proc.stderr.readline(), proc.stdout.readline()]): print(''read {!r}''.format(await line)) event_loop = asyncio.get_event_loop() try: event_loop.run_until_complete(run_df()) finally: event_loop.close()

redirigiendo el de stdout

Basado en tu ejemplo, esta es una solución realmente simple. Simplemente redirige el stderr a la salida estándar y solo se lee la salida estándar.

from subprocess import Popen, PIPE, CalledProcessError, run, STDOUT import os def execute(cmd): with Popen(cmd, stdout=PIPE, stderr=STDOUT, bufsize=1) as p: while 1: print(''waiting for a line'') print(p.stdout.readline()) cmd2 = ["python", "-u", "-m", "http.server"] execute(cmd2)


Podría implementar el comportamiento sin búfer en el nivel del sistema operativo.

En Linux, podría envolver su línea de comando existente con stdbuf :

stdbuf -i0 -o0 -e0 YOURCOMMAND

O en Windows, puede ajustar su línea de comando existente con winpty :

winpty.exe -Xallow-non-tty -Xplain YOURCOMMAND

No estoy al tanto de las herramientas del sistema operativo neutral para esto.


Si desea leer continuamente desde un subproceso en ejecución, debe hacer que la salida del proceso sea sin búfer. Su subproceso es un programa de Python, esto se puede hacer pasando -u al intérprete:

python -u -m http.server

Así es como se ve en una caja de Windows.