Mostrando entradas con la etiqueta python. Mostrar todas las entradas
Mostrando entradas con la etiqueta python. Mostrar todas las entradas

miércoles, 17 de agosto de 2016

Py2exe y las DLLs de Windows

Creo que todo el que haya alguna vez trabajado con el Py2exe para "convertir" un programa en Python a un EXE para Windows se ha dado cuenta de lo mal que gestiona las DLLs.

A esa mala gestión hay que añadir unos mensajes de error nada esclarecedores. Y además, algunas de las DLLs que Py2exe toma del sistema, son dependientes del propio sistema. Lo que hace que si compilas en un Windows 10 (por ejemplo) no te funcione el software en un Windows 7.

Ayer me topé con el siguiente error:

Traceback (most recent call last):
  File "c:\Python27\lib\site-packages\gi\__init__.py", line 42, in <module>
    from . import _gi
  File "gi\_gi.pyc", line 12, in <module>
  File "gi\_gi.pyc", line 10, in __load
ImportError: DLL load failed: El sistema operativo no puede ejecutar %1.


El software si lo compilaba en Windows 10 funcionaba en Windows 10 pero no en Windows 7, y viceversa. Luego estaba claro que había alguna DLL del sistema y que sobraba pero ¿cuál?

Con Winmerge comparé los directorios "lib" de ambas compilaciones y vi que sólo había 3 DLLs distintas, con lo que sólo tuve que probar a ir reemplazando una a una. Al final resultó que la DLL que molestaba era IPHLPAPI.DLL.

Así que simplemente hay que borrar esa DLL tras la compilación para que el software compilado en Windows 7 funcionara en Windows 10.

        # Delete System dependent DLLs
        os.remove(os.path.join('dist', 'lib', 'IPHLPAPI.DLL'))


Y luego para que el software compilado en Windows 10 funcionara en Windows 7, tuve que borrar las otras dos DLLs que eran distintas:

        # Delete System dependent DLLs
        os.remove(os.path.join('dist', 'lib', 'IPHLPAPI.DLL'))
        os.remove(os.path.join('dist', 'lib', 'DNSAPI.DLL'))
        os.remove(os.path.join('dist', 'lib', 'MPR.dll'))


RESUMIENDO: Hay que borrar aquellas DLLs que sean distintas (si hay más DLLs al compilar en un Windows que en otro no importa, las que estorban son las que son distintas).

Salu2 a to2


jueves, 18 de diciembre de 2014

Sockets no bloqueantes en Python

Hola a todos. El uso de sockets es sin duda uno de los recursos más utilizados que hay, y también uno de los peor utilizados.

Los sockets pueden configurarse como bloqueantes (por defecto) o hacerlos no bloqueantes mediante "fcntl" con el "flag" O_NONBLOCK. Y la diferencia es brutal.

Un socket bloqueante detendrá la ejecución del programa cuando se haga una llamada a "recv" y no haya datos para recibir. Y cuando se reciban los datos la ejecución continuará.

Mientras que un socket bloqueante, al hacer "recv" si hay datos se obtienen y si no los hay se devuelve una excepción con un valor "errno" de 11 (EAGAIN), que nos indicará básicamente un "prueba otra vez".

Pues bien, en un programa con sockets supuestamente bloqueantes vi que se comportaban como no bloqueantes sin razón aparente y he tardado 3 días en encontrar la solución.

El problema era debido al "timeout" por defecto. Me explico. En Python el "timeout" por defecto para los sockets está establecido a "None". Esto significa que si se intenta establecer una conexión con un host remoto y la conexión no se establece vaya usted a saber por qué, el programa se queda esperando indefinidamente ya que no hay un timeout.

Así, que en su día hice el cambio de establecer un "timeout" por defecto de 30 segundos. Y un tiempo después (hace 3 días), se detectó un problema derivado de esta decisión.

Y es que al establecer el timeout los sockets se vuelven no bloqueantes. Pero es más, muchos ni siquiera esperan esos 30 segundos.

¿Que cómo es esto posible? Porque no todos los sockets son del mismo tipo ni se usan para lo mismo.

Los sockets que establecen una conexión con un host remoto sí funcionan correctamente (con su timeout de 30 segundos), pero los sockets que se utilizan para la comunicación entre procesos con la librería "multiprocessing", estos funcionan como sockets no bloqueantes y no respetan el timeout de 30 segundos.

Así que la comunicación entre procesos "con multiprocessing.Pipe", y "multiprocessing.Listener" (usado dentro "multriprocessing,reduce_handle"), sencillamente no funcionaba.

Con lo que el resultado es que he tenido que quitar el timeout por defecto de 30 segundos, dejarlo como "None", y poner el timeout de 30 segundos sólo a aquellos sockets que me interesaban.

RESUMIENDO: No se puede establecer un timeout por defecto si usas comunicación entre procesos con la librería "multiprocesing"

Salu2 a to2