Liberé logassert 5

Estoy feliz de traerles una nueva versión de logassert, un mecanismo simple para verificar en los tests de un programa en Python que los logs se realizaron correctamente.

Porque todes sabemos que tenemos que chequear los logs de nuestros programas, ¿cierto? Con logassert esto es muy fácil.

Esta nueva versión trae la funcionalidad de poder usar logassert como un fixture de pytest, con semánticas totalmente renovadas (para las estructuras de "unittest clásico", logassert casi no cambió, por compatibilidad).

Entonces, para pytest todo lo que se necesita es declarar logs en los argumentos del test (funciona como cualquier otro fixture), y luego chequear (usando assert, como es normal con pytest) si una linea específica está en los logs para un nivel específico.

Miren este ejemplo, con varias lineas logueadas, y un control específico:

logger.info("Starting system")
places = ['/tmp/', '~/temp']
logger.debug("Checking for config XYZ in all these places %s", places)
logger.warning("bad config XYZ")

assert "bad config XYZ" in logs.debug

Vean como el mensaje de falla es muy útil:

assert for regex 'bad config XYZ' check in DEBUG, failed; logged lines:
       INFO      'Starting system'
       DEBUG     "Checking for config XYZ in all these places ['/tmp/', '~/temp']"
       WARNING   'bad config XYZ'

Puede instalar logassert desde PyPI. El proyecto está en Github.

¿Les interesa más detalle de como funciona?

Como les dije arriba, pueden incluir el fixture logs y después directamente usar assert. Ejemplo:

def test_bleh(logs)
    (...)
    assert "The meaning of life is 42" in logs.debug

En verdad, la linea que escriben es una expresión regular, entonces pueden hacer (en caso de que sepan exactamente cual es el significado de la vida):

assert "The meaning of life is \d+" in logs.debug

La cadena indicada se busca dentro de las lineas logueadas, no tiene que ser exactamente la linea completa. Si quieren eso, indíquenlo como en cualquier expresión regular:

assert "^The meaning of life is \d+$" in logs.debug

De forma similar, también pueden verificar que esté al principio o al final de la linea logueada.

NOTA: el mensaje verificado es el final, luego de que el system de logging reemplazó todos los parámetros indicados en la cadena indicada.

Si quieren verificar que un texto fue logueado sin importar en qué nivel, sólo hagan:

assert "The meaning of life is 42" in logs.any_level

Para verificar que un texto NO fue logueado, sólo usen la sintáxis de Python! Por ejemplo:

assert "A problem happened" not in logs.error

Si no les gusta las expresiones regulares, importen Exact de logassert y envuelvan la cadena con eso. Por ejemplo, en este caso el .. significa "dos puntos", no hace nada a nivel expresión regular:

assert Exact("The meaning of life is ..") in logs.any_level

Para pedir ayuda, si tienen alguna pregunta, o encuentran algún detalle, por favor abran un ticket.

¡Gracias por vuestro tiempo!

Comentarios Imprimir

Asegurando energía

Cuando empecé a laburar desde casa, hace unos once años y medio, me di cuenta que me iba a tener que comprar bocha de cosas: una buena silla, escritorio, etc. Pero algo que no pensé al principio, y que tuve que comprar al poco tiempo, fue una UPS.

Una UPS (Uninterruptible Power Supply, o Fuente de Energía Ininterrumpible) permite seguir trabajando en la computadora aunque se corte la electricidad en tu casa, y además en alguna medida te protege contra picos o bajones de tensión, lo cual también es una buena idea.

Hay un millón de modelos, y no es algo novedoso, pero yo nunca había tenido una UPS hogareña. En su momento investigué un poco y me terminé comprando una TRV 650A.

La viejita

Siempre le enchufé la compu principal, el monitor, y el router de internet.

Al principio tiraba un montón de tiempo cuando se cortaba la electricidad. Luego la batería se fue poniendo vieja, y rendía menos. En algún momento empecé a dejar de laburar cuando se cortaba la electricidad, pero tener la UPS me permitía apagar la máquina de forma ordenada, y dejar el resto de energía que le quedaba para alimentar el router (que consume muchísimo menos que la compu y el monitor, entonces tiraba bastante tiempo más).

Tipo por 2016 decidí cambiarle la batería interna. Resulta que la gente de TRV está en una esquina de CABA medianamente cerca de casa, así que se las llevé a ellos. Le cambiaron la batería, claro, pero también le pegaron una revisada general. La renové, bah.

Pero pasó tiempo. Y el otro día se volvió a cortar la electricidad y se apagó todo al toque. Decidí que era momento de hacer un cambio, y compré una UPS nueva.

Como TRV me funcó bien, ni lo pensé, les compré a ellos. Algo un poco más grande, que no viene mal, la TRV 1200.

La nueva

La enchufé, y parece andar piola.

Decidí también conectarla a la compu. Probé con el software que trae, que teoricamente soporta Linux, pero es un coso escrito en Java que nunca me encontró la UPS conectada :(

Me puse a buscar algo por ahí, y resulta que hay una solución bastante interesante: nut (por Network UPS Tools, pero que también soportan UPSs conectadas por USB). La instalación fue trivial (está en los repos de Ubuntu), pero la configuración no tanto, porque hay capas y partes móviles, porque parece que el mundo de las UPSs es un toque complejo :p.

Algo que me costó fue encontrar "qué tipo de UPS tenía"... como con todas las cosas fabricadas integrando componentes, la marca "exterior" muchas veces no tiene nada que ver con "la plaqueta interior". Acá me ayudó mucho hacer un lsusb y darme cuenta que el controlador interior era de Powercom, con algunos números más que me ayudaron a encontrar el driver adecuado en este listado.

Fuí siguiendo esta explicación y aunque algunas cosas no son exactamente igual, parece que quedó todo configurado y andando.

Ahora tengo todo prolijo, y puedo hacer cosas como esta:

$ upsc upstrv | grep voltage
input.voltage: 218.0
input.voltage.nominal: 220
output.voltage: 218.0
output.voltage.nominal: 220

:D

Comentarios Imprimir

Jugando virtual (y no tanto)

Es obviamente un resultado de la pandemia el que estemos todes encerrades y no podamos salir a hacer nada.

Bueno, algunas cosas esenciales sí, pero no a todo lo que estábamos acostumbrades. Por ejemplo, juntarnos con amigues, charlar, jugar algún juego de mesa...

La parte de "charlar" es facilmente resoluble. Sí, podés hacer una videoconferencia (Jitsi, Google Meet, Zoom, etc), pero también podés agarrar el teléfono ("levantar el tubo") y hacer una llamada "de las de antes" :p.

La parte de jugar algún juego de mesa es más complicada. O imposible dependiendo del juego. Pero por suerte hay algunas plataformas online que (con alguna registración previa, pero en general gratis) dan el servicio de jugar algún determinado juego.

Entonces, lo único que hay que hacer es coordinar con algunes amigues encontrarse en un determinado horario en alguna videoconferencia, y jugar un rato con alguna de estas plataformas. Tené en cuenta que algunas necesitan un "setup" o configuración inicial, que es mejor intentarlo antes así no se pierde el tiempo cuando todes están ansioses de jugar, y todo sale más dinámico.

Este post es para contarte, entonces, algunos de estos juegos que fuí encontrando y jugando en distintos grupos...

  • Dominion [plataforma, instrucciones/tutorial]: Un gran juego de cartas en el que en cada turno se puede hacer una acción y una compra (al menos), balanceando todo entre principalmente tres tipos de cartas: las de victoria (es lo que hay que sumar para ganar el juego, querés comprar muchas de estas), las de tesoro (sirven como moneda para adquirir otras cartas, querés comprar muchas de estas) y de acción (generan efectos como más compras, dinero extra, tirar maldiciones, etc; querés comprar muchas de estas). Obviamente el qué comprar y cuándo es el equilibrio que tenés que tener en el juego...
Dominion Online
  • Drawful 2 [sitio]: Una especie de pictionary online, pero mezclado con que todes tienen que adivinar lo que se intentó dibujar. Está bueno porque aunque juegues en la compu podés entrar a dibujar con el teléfono (es más fácil dibujar moviendo el dedo en el celu que con el mouse). Ojo que este hay que pagarlo (sólo una persona, la que hostea el juego).
  • Cards Against Humanity [plataforma, instrucciones]: Un clásico de los juegos de cartas, lleva al límite la incorrección política. Su versión online tiene la (GRAN) ventaja que podés escribir tu propia respuesta en el momento, con lo cual podés darle una vuelta de rosca más al juego.
  • Tutti frutti [plataforma]: También llamado "Basta" en otras regiones. En esta plataforma, aunque te tenés que registrar (gratis) para hacerlo, podés armar el juego con las categorías (o "columnas") que quieras. Entonces, podés jugar algo clásico como "colores", "paises", "nombres", etc, o podés elegir categorías desopilantes que te ofrece el sitio (o armar las tuyas propias), como "te amo, pero...", "excusas para no bañarte", "lugares para esconder un cadáver", etc...
  • Coronabingo [plataforma]: Eso, un bingo, por si querés algo tranquilo y sencillo.
  • Apocalipsis higiénico [plataforma]: A diferencia de los anteriores no simula un juego de mesa, sino una sala de escape. A mí me encantan las salas de escape "físicas" y le tenía medio desconfianza a probar una virtual, porque gran parte de lo lindo de la sala de escape es interactuar con objetos físicos de la misma manera que siempre hicimos en la compu con las aventuras gráficas. Debo reconocer que este Apocalipsis higiénico estuvo muy, muy bueno. Con una historia interesante, con desafíos raros y divertidos, y hasta con "pistas" bien pensadas por si te trababas en algún paso. Me encantó.

Hay muchos más para explorar en BoardGame Arena, pero es algo que yo no probé (todavía).

Les quiero dejar como bonus track un juego NO virtual (aunque se puede jugar de forma cooperativa, pero todavía no lo intenté). Que hay que comprar e instalar. Pero es genial, para mí el descubrimiento de la década. Es el Portal 2.

Portal 2

Es basicamente un juego de resolución de acertijos. Mezclado con el modo disparos en primera persona (FPS). Inmerso en una gran, gran historia. Y con el condimento maravilloso (la base del juego, claro) que se pueden crear portales a voluntad. Esto le da una fantástica posibilidad creativa a la hora de pensar las resoluciones de cada ejercicio. Con Felu ya lo terminamos, tipo en un par de semanas, nos alucinó. Ahora estamos viendo cómo jugarlo de forma cooperativa.

Es en este momento que les aclaro que sí, hay que comprarlo, pero ahorita mismo está en oferta en Steam, sale $26. Eso son 26 PESOS ARGENTINOS, ¡una verdadera ganga!

Con Felu también nos encantó este fantástico resumen del juego, pero ¡ojo, spoiler alert! les cuenta toda la historia, así que si lo piensan jugar, ¡no lo vean! En fin, están avisades. Es este.

Comentarios Imprimir

Salió fades 9.0

¡Tenemos una nueva versión de fades en la calle!

Ya deberían saber qué es fades... pero por las dudas: es un sistema que maneja automáticamente los virtualenvs en los casos que uno normalmente encuentra al escribir scripts y programas pequeños de Python, e incluso ayuda a administrar proyectos grandes. Crea automáticamente un nuevo virtualenv (o reusa uno creado previamente) instalando las dependencias necesarias, y ejecutando el script dentro de ese virtualenv.

Con Nico estamos muy contentos porque esta versión nos trae algunas mejoras piolas que veníamos esperando tener:

  • Hace que pip se actualice automáticamente a la última versión en la creación del virtualenv (a menos que se indique lo contrario).
  • Provee la opción --freeze, que graba la info detallada de los paquetes del virtualenv, para duplicar instalaciones futuras.
  • Extiende y normaliza el comportamiento del parámetro -x/--exec para soportar paths arbitrarios.
  • Crea la opción --autoimport para importar automáticamente las dependencias instaladas al entrar al intérprete interactivo.

También agregamos ejemplos y descripciones a la documentación, mejoramos el parseo de argumentos cuando fades se usa en el shebang y se mejoró la infrastructura en general (mejores pruebas, soporte multiplataforma, etc).

Toda la documentación en Read The Docs. Y recuerden que pueden pasar a hacer consultas sobre fades en el grupo de Telegram correspondiente.

Comentarios Imprimir

La próxima evolución del backup

La frase "hay que hacer backup" es una de esos mantras que uno repite y repite pero sobre los que raramente acciona.

A menos que, como a mí, se les haya roto hardware y hayan perdido información. Ya hace muchos años que hago backup y tengo distintos métodos para salvaguardar mi data.

Así y todo, no lo tenía 100% resuelto. Tengo mucha información que es imposible recuperar (ejemplo: ¡fotos!) en dos discos en casa y "en la nube", pero hay otro montón que sólo tenía localmente.

Y sí, uno piensa que en el caso de "se me prendió fuego la casa" o "me entraron a robar y se llevaron las compus" toda la información que no tenía backup externo pasa a un segundo plano en importancia. Pero no, porque aparte de que te pasó alguna de esas "catástrofes", encima perdiste la info esa, de la cual puede depender tu laburo...

"No te preocupes, tengo una copia en un disco y la otra en la habitación de al lado" - Tu casa ↑

En el pasado les comenté que tenía la intención de hacer backups externos en algo como Amazon Glacier, y hace un tiempo empecé a ver qué onda, calcular un poco qué costos tendría, etc.

Justo en esa época me aumentaron el precio del servicio que pago en Dropbox, en el que tengo 2 TB de almacenamiento, entonces tomé la decisión de usar Dropbox como "lugar remoto en dónde hacer backup": no tenía que contratar otro servicio, no tenía que incrementar mis costos al respecto, no tenía que aprender a usar otro backend, etc. Todo redondo.

Entonces apunté a un directorio bajo Drobox el rdiff-backup que venía ejecutando (que me dejaba copias diferenciales de mis datos importantes en otro disco).

Y ahí empezó un pequeño descalabro que más o menos pude manejar. Es que rdiff-backup me copió al Dropbox un trillón de pequeños archivos con variadísimos nombres. No es culpa de esa herramienta, claro, sino que es esa justamente la info que me interesa resguardar.

Pero Dropbox probó ser bastante flojito.

Por un lado, algo que ya sabía: hay un montón de limitaciones que tiene Dropbox con respecto a los nombres de los archivos. Parte de esas limitaciones son porque el sistema de archivos de Windows es más limitado, y Dropbox te transmite esas limitaciones incluso cuando une no esté replicando esos archivos en Windows.

Y luego se le complicó manejando el trillón de archivitos que le aparecieron de repente. No era un problema de "ancho de banda" (que no estaba saturada), sino de cantidad de archivos. Más allá que le tuve que levantar un límite del sistema operativo al proceso, tuve que matarlo y volver a levantarlo mil veces hasta que terminó de procesar todo. Ahora quedó más o menos andando: a veces se cuelga, y cuando arranca tarda un buen, buen rato en estar disponible.

Se colgó

Hace un par de semanas me cansé y opté por hacer algo al respecto.

Y armé un script en Python que lo que hace es agrupar archivos y directorios en "paquetes con nombres sanos", y después mete eso directamente en Dropbox. Ok, lo resumí demasiado, vamos con más detalles...

Al script se le pasa un archivo de configuración que tiene tres datos obligatorios:

  • rootdir: el directorio raiz del árbol completo que se quiere hacer backup
  • builddir: un lugar en donde se van a ir dejando todas las estructuras intermedias
  • syncdir: el lugar final a donde copiar todo lo generado

Entonces al arrancar trabaja leyendo ese árbol desde rootdir, dejando unos comprimidos (.tar.gz) en builddir. Esos comprimidos son todos los archivos de ese directorio raiz, más cada uno de sus directorios (sub-árboles enteros), teniendo en cuenta siempre de sanitizar los nombres para que sean todos válidos para Dropbox.

Luego de recorrer todo, manda lo que armó al destino final en Dropbox (syncdir) directamente, sin usar rsync-diff, porque Dropbox ya me da la funcionalidad de "historial" de los archivos (para la cuenta que tengo, 30 días).

Dos "grandes detalles"...

Por un lado, hay archivos o directorios que quiero ignorar; un ejemplo típico en mi caso es ~/devel/reps, porque todo lo que tengo ahí está en github, o launchpad, o etc. Bueno, en ese archivo de configuración que mencioné antes se puede indicar una lista de "todo lo que se quiere ignorar".

Por otro lado, hay directorios que no tiene sentido empaquetar directamente, sino que conviene explorarlos un poco más. Por ejemplo, no tiene sentido empaquetar .local directamente, ni siquiera .local/share, sino que lo más piola es tener .local/share como directorios normales y empaquetar los quichicientos directorios que están ahí adentro, por separado. Para esto el archivo de configuración incluye algo llamado "niveles de agrupamiento": para dicho ejemplo tendríamos: .local: 2

La idea general es tener "paquetes", ni muy chicos (serían miles, en el extremo volveríamos a la situación original), ni demasiado grandes (porque si tenemos un paquete de 5GB, y entre backup y backup cambiamos un solo byte de eso, el archivo nos queda diferente). Por eso por ejemplo lo que hay adentro .local/share conviene empaquetarlo todo separado, porque lo más probable es que poco de eso cambie de backup a backup.

Para ayudarnos a analizar esa situación (y ver si nos conviene ignorar algo, o cambiar los niveles de agrupamiento de algún directorio), el script al terminar nos da un resumen de tamaños de archivos y sus estados con respecto al backup anterior. De esta manera podemos detectar si tenemos algún directorio grande que está cambiando todo el tiempo, y que quizás querramos discriminar en sus subdirectorios.

Si piensan usarlo, y se les complica con algo o necesitan algún detalle, me avisan!

Comentarios Imprimir