Que tus "futures" de larga duración no impidan que tu programa Python termine

En Python: Olvídate de los hilos y usa "futures" defiendo el uso de Futures en vez de hilos tradicionales por su mayor facilidad de uso, pero vemos que no existe una forma simple de marcar un Future como Daemon. Es decir, que el programa no terminará su ejecución hasta que todos los Futures se completen.

Esto no está mal. De hecho nos asegura que los recursos utilizados por los Futures se liberen de forma ordenada. Puede ser necesario borrar ficheros temporales o desconectarse de la base de datos de forma adecuada, por ejemplo. [1]

Con el modelo de actores que empleo habitualmente, los Futures no terminan nunca. Se pueden quedar ahí esperando eternamente a que alguien les pida algo. Por supuesto es posible definir un mensaje global que solicite un suicidio colectivo, pero ello implica la gestión de un mensaje adicional y el registro de todos los actores en algún sitio. Ninguna de las dos cosas es realmente un problema, pero en muchos casos no necesitamos estas complejidades.

Un ejemplo común es el caso de Futures de larga duración esperando trabajo mientras el hilo principal supervisa que no falle ninguno. Si alguno muere inesperadamente, el hilo principal lo detecta, notifica la excepción y el programa termina.

Existen dos posibilidades básicas, a priori:

Leer más…

Python: Olvídate de los hilos y usa "futures"

Soy un usuario masivo de hilos a la hora de programar en Python. Mi gestión de la complejidad y las condiciones de carrera se basa en ser cuidadoso y, cuando el problema lo permite, usar colas u otros mecanismos de sincronización y comunicación. Podría decirse que uso un modelo de actores en Python.

Con cuidado y experiencia me las apaño bastante bien. Es raro que tenga problemas.

Python 3.2 introdujo el concepto de Futures, un tipo de paralelismo que abstrae el uso de hilos o multiprocesamiento. En mi caso personal lo que más me interesa es la facilidad para seguir la pista a los diferentes hilos que lanzo, más concretamente para detectar de forma sencilla cuándo terminan correctamente o cuándo mueren debido a una excepción.

Antes de los futures, mis hilos estaban contenidos en algo similar a:

Leer más…

Cómo acceder al "code object" de una clase Python

Una de las características más interesantes de Python es su capacidad de introspección. Recientemente necesité acceder al code object de una clase Python y lo cierto es que no es tarea trivial.

Contexto: Creo una clase caché con persistencia transparente. Normalmente incluiría un número de versión en la persistencia y un mecanismo de migración para que pueda reutilizar los datos persistentes si modifico el código que interactúa con ella. Eso requiere estar atento y actualizar la versión y el proceso de migración cuando hay cambios relevantes en el código. Esto es manual. En este caso concreto no me interesa tener un mecanismo de migración de versiones con su mantenimiento manual. Dado que esto es una caché, si hay un cambio de código lo que quiero es que se detecte y se ignore la persistencia antigua.

He encontrado tres formas de acceder al code object de una clase Python:

Leer más…

Por qué es mala idea (a primera vista) desplegar IPv6 con 'Unique Local Address'

En Cómo prestar servicio IPv6 a través de OpenVPN explico cómo configurar OpenVPN para dar servicio IPv6. Los entendidos del tema se habrán sorprendido por el uso de NAT cuando estoy utilizando direcciones IPv6

La práctica habitual sería o bien no usar NAT en absoluto por ser innecesario o bien usar NAT para poder emplear internamente direcciones Unique Local Address.

En una primera versión de todo esto empleé direcciones Unique Local Address, concretamente fd00::/8. Esto me permitía entregar a cada uno de mis usuarios OpenVPN un rango ::/48. Estupendo.

Una vez montado, sin embargo, llega el jarro de agua fría:

Leer más…

Cómo prestar servicio IPv6 a través de OpenVPN

Cansado de que mi proveedor de Internet no me ofrezca IPv6 en mi red doméstica de forma nativa, decidí tomar cartas en el asunto.

Hace una década que navego con mi portátil y mi móvil a través de OpenVPN. Así no tengo que preocuparme de direcciones IP dinámicas o que la WIFI que esté utilizando sea una red hostil.

Las versiones recientes de OpenVPN soportan IPv6.

Veamos los cambios que realizo en mi configuración actual:

Leer más…

Parche de autenticación SMTP para Mailman 2

Increíblemente Mailman 2 no ha incluído autenticación SMTP hasta la versión 2.1.23 publicada el 27 de agosto de 2016. Y eso a pesar o, tal vez, a causa de que el parche es trivial y de que todo el mundo tenía su propia versión. Por ejemplo:

Yo también he tenido mi versión propia durante ocho años:

--- SMTPDirect.py.OLD   2017-11-24 00:38:36.601046958 +0100
+++ SMTPDirect.py       2017-11-24 00:38:07.104575066 +0100
@@ -62,6 +62,14 @@
     def __connect(self):
         self.__conn = smtplib.SMTP()
         self.__conn.connect(mm_cfg.SMTPHOST, mm_cfg.SMTPPORT)
+
+        # MI CODIGO - jcea@jcea.es - 20091016
+        if mm_cfg.SMTPTLS :
+            self.__conn.starttls()
+        if mm_cfg.SMTPUSER :
+            self.__conn.login(mm_cfg.SMTPUSER, mm_cfg.SMTPPASSWORD)
+        #
+
         self.__numsessions = mm_cfg.SMTP_MAX_SESSIONS_PER_CONNECTION

     def sendmail(self, envsender, recips, msgtext):

Me alegro de que ya no sea necesario.

Actualización 20171205: Tras ser un poco pesado (no mucho), he conseguido que pkgsrc actualice a la versión 2.1.25 de Mailman. Estará disponible en la distribución 2017Q4.

Uso de "nullmailer" en zonas nativas SmartOS

Las zonas nativas SmartOS incorporan un servidor de correo Postfix que hay que configurar correctamente para procesar los mensajes que puedan generarse de forma local como resultado de ejecuciones fallidas del demonio CRON o servicios similares. Enfrentado a este problema, lo que necesito exclusivamente es un sistema de "mail forwarding" que envíe todo el correo generado de forma local a mi servidor de correo normal, que reside en otra máquina diferente. No quiero inteligencia de ningún tipo, tan solo un simple "mail forwarding" ciego.

Se puede configurar Postfix para que haga "mail forwarding", de hecho es una configuración frecuente, pero me parece matar moscas a cañonazos. Preguntando sobre el particular en el canal IRC de SmartOS, muy amigable, se me sugirió un paquete disponible directamente en Pkgsrc: nullmailer.

Nullmailer hace exacta y exclusivamente lo que necesito: enviar a un servidor de correo remoto todo el correo que se genere de forma local.

No obstante, hay un problema: Nullmailer contiene un bug que hace que los mensajes generados por CRON, precisamente mi caso de uso, se reciban sin cabeceras; las cabeceras acaban como cuerpo del mensaje. Es decir, el mensaje que te llega es algo de este tipo:

From root Mon Nov 14 23:42 UTC 2016
To: root
Subject: Cron <root@5ca32ab7-57b7-4af4-8740-69fcd29cef86> echo hi
Auto-Submitted: auto-generated
X-Mailer: cron (SunOS 5.11)
X-Cron-User: root
X-Cron-Host: 5ca32ab7-57b7-4af4-8740-69fcd29cef86
X-Cron-Job-Name: echo hi
X-Cron-Job-Type: cron
MIME-Version: 1.0
Content-Type: text/plain
Content-Length: 3

hi

El problema es que a nullmailer no le gustan los mensajes cuya primera línea empieza por "From " o ">From ", aunque son legales.

La solución evidente sería parchear nullmailer, pero no quiero modificar paquetes binarios de pkgsrc. En vez de ello lo que he hecho es un wrapper en Python que filtra el mensaje entrante y simplemente se salta la primera línea si empieza por "From " o ">From ".

Leer más…