La aplicación empaquetada PyInstaller funciona bien en el modo de consola, se bloquea en el modo de ventana

Estoy construyendo una aplicación bastante compleja usando Python y PySide. Finalmente se acerca el día del lanzamiento, así que quiero construir esta aplicación como un exe.

Sin embargo, tengo un problema extraño en mis manos. He usado PyInstaller (usando la versión 2 por cierto) en el pasado y nunca me había pasado esto.

Básicamente, cuando --console la aplicación con el indicador --console , funciona bien, pero abre la ventana de la consola. Cuando compilo la aplicación con el indicador de ventana ( -w ), no funciona bien. Comienza y todo, pero hay todas estas fallas extrañas. Por ejemplo, cargar un archivo de texto a menudo genera el error BadFileDescriptor (que no ocurre en el modo de consola) y la aplicación se bloquea después de realizar una determinada tarea. Lo que es peor es que la tarea es un bucle, y funciona bien la primera vez, pero cuando comienza a funcionar de nuevo, se bloquea.

Cuando miré el archivo minidump hubo algunos errores sobre la violación de acceso a la memoria del archivo QtGui4.dll. Una vez más, esto no sucede en el modo de consola.

¿Alguien tiene alguna idea?

El error BadFileDescriptor y, en consecuencia, la violación del acceso a la memoria se deben al hecho de que la BadFileDescriptor de las aplicaciones en modo de ventana es un búfer de tamaño fijo. Por lo tanto, si tiene que escribir en stdout , ya sea con print o sys.stdout directamente, después de algún tiempo verá esos errores.

Puedes arreglar esto por:

  1. Eliminar / comentar las escrituras en la salida stdout
  2. Usando el logging lugar de imprimir a la salida estándar
  3. Redireccionando stdout al inicio de la ejecución de su aplicación. Esta es la solución que requiere que se modifique menos código, aunque creo que la mejor opción sería cambiar las declaraciones de depuración al logging .

Para redireccionar stdout puedes usar este tipo de código:

 import sys import tempfile sys.stdout = tempfile.TemporaryFile() sys.stderr = tempfile.TemporaryFile() 

Justo antes de ejecutar tu progtwig. También puede utilizar algún objeto personalizado para colocar la salida en archivos de “registro” o lo que sea, lo importante es que la salida no debe llenar el búfer de tamaño fijo.

Por ejemplo, podría hacer algo como esto para poder aprovechar el módulo de logging sin cambiar demasiado el código:

 import sys import logging debug_logger = logging.getLogger('debug') debug_logger.write = debug_logger.debug #consider all prints as debug information debug_logger.flush = lambda: None # this may be called when printing #debug_logger.setLevel(logging.DEBUG) #activate debug logger output sys.stdout = debug_logger 

La desventaja de este enfoque es que la print ejecuta más llamadas a stdout.write para cada línea:

 >>> print 'test' DEBUG:debug:test DEBUG:debug: 

Si lo desea, probablemente pueda evitar este tipo de comportamiento escribiendo una función de write real que llame a the_logger.debug solo con “líneas completas”.

De todos modos, creo que este tipo de solución solo debe ser temporal, y debe usarse solo antes de transferir las print a las llamadas a logging.debug .

(Obviamente, los registradores deben escribir en un archivo y no en stdout para evitar los errores).