Búsqueda por prefijos tolerante a errores

Python — Jueves 30 de Abril de 2015, 18:16


Hace un tiempo les hablé de un árbol que hice para sacar prefijos de palabras.

En el laburo estoy estudiando la forma de hacer un autocompletador. Entonces, luego de leer cosas por ahí, decidí probar ese árbol que ya tenía hecho.

Nunca le había tirado tantos datos, pero la verdad es que salió andando de perlas.

Por otro lado, tenía un detalle que necesitaba solucionar: yo quería que la búsqueda de palabras soportara errores en la escritura. O sea, que si uno buscara "maise", encontrara "maizena".

Encontré un paper bastante loco, Efficient Error-tolerant Query Autocompletion, pero que mostraba la forma de soportar errores al buscar palabras completas, no prefijos. Igual, apliqué ideas de ahí, y en un par de días de laburo conseguí lo que quería. Pero, al cargar el millón y medio de registros que tengo que cargar, ¡explotaba por memoria!

Luego de algunas optimizaciones obvias, se me ocurrió lo de deduplicar los subtrees internos. ¿Qué es deduplicar? Deduplicar es la acción por la cual si tengo un objeto A, y luego tengo otro B, que resulta ser igual a A, puedo usar el A directamente en ambos casos, descartando B (libera memoria), y listo.

Deduplicar diccionarios no es un asunto trivial. Tiré el asunto en la lista de PyAr, y en pocas horas logré que todo funcione correctamente. Ahora no sólo no explota, sino que ocupa bastante poca memoria!

    Memory usage after loading the tree: rss: +586 MB  vms: +586 MB
    Time to load the tree: 327190.99 msec
    <WordTree at 3068071276 [tau=1]: 1478347 words 30015540 (2201293) nodes (unique)>

Millón y medio de palabras, 30 millones de nodos (de los cuales 2.2 millones son únicos), ocupando 590 MB de memoria. Nada mal, ¿no? Que tarde 5.5 minutos en armar toda la estructura es un problema, la semana que viene voy a mirar eso bien.

Todo el código, acá.


Metaclasses in Python, the easy way (a real life example)

Python — Jueves 12 de Marzo de 2015, 00:24


They say that metaclasses make your head explode. They also say that if you're not absolutely sure what are metaclasses, then you don't need them.

And there you go, happily coding through life, jumping and singing in the meadow, until suddenly you get into a dark forest and find the most feared enemy: you realize that some magic needs to be done.


The necessity

Why you may need metaclasses? Let's see this specific case, my particular (real life) experience.

It happened that at work I have a script that verifies the remote scopes service for the Ubuntu Phone, checking that all is nice and crispy.

The test itself is simple, I won't put it here because it's not the point, but it's isolated in a method named _check, that receives the scope name and returns True if all is fine.

So, the first script version did (removed comments and docstrings, for brevity):

    class SuperTestCase(unittest.TestCase):

        def test_all_scopes(self):
            for scope in self._all_scopes:
                resp = self._check(scope)
                self.assertTrue(resp)

The problem with this approach is that all the checks are inside the same test. If one check fails, the rest is not executed (because the test is interrupted there, and fails).

Here I found something very interesting, the (new in Python 3) subTest call:

    class SuperTestCase(unittest.TestCase):

        def test_all_scopes(self):
            for scope in self._all_scopes:
                with self.subTest(scope=scope):
                    resp = self._check(scope)
                    self.assertTrue(resp)

Now, each "sub test" internally is executed independently of the other. So, they all are executed (all checks are done) no matter if one or more fail.

Awesome, right? Well, no.

Why not? Because even if internally everything is handled as independent subtest, from the outside point of view it still is one single test.

This has several consequences. One of those is that the all-inside test takes too long, and you can't know what was going on (note that each of these checks hit the network!), as the test runner just show progress per test (not subtest).

The other inconvenient is that there is not a way to call the script to run only one of those subtests... I can tell it to execute only the all-inside test, but that would mean to execute all the subtests... which, again, takes a lot of time.

So, what I really needed? Something that allows me to express the assertion in one test, but that in reality it were several methods. So, I needed something that, from a single method, reproduce them so the class actually had several ones. This is, write code for a class that Python would find different. This is, metaclasses.


Metaclasses, but easy

Luckily, since a couple of years ago (or more), Python provides a simpler way to achieve the same that could be done with metaclasses. This is: class decorators.

Class decorators, very similar to method decorators, receive the class that is defined below itself, and its response is considered by Python the real definition of the class. If you don't have the concept, you may read a little here about decorators, and a more deep article about decorators and metaclasses here, but it's not mandatory.

So, I wrote the following class decorator (explained below):

    def test_multiplier(klass):
        """Multiply those multipliable tests."""
        for meth_name in (x for x in dir(klass) if x.startswith("test_")):
            meth = getattr(klass, meth_name)
            argspec = inspect.getfullargspec(meth)

            # only get those methods that are to be multiplied
            if len(argspec.args) == 2 and len(argspec.defaults) == 1:
                param_name = argspec.args[1]
                mult_values = argspec.defaults[0]

                # "move" the usefult method to something not automatically executable
                delattr(klass, meth_name)
                new_meth_name = "_multiplied_" + meth_name
                assert not hasattr(klass, new_meth_name)
                setattr(klass, new_meth_name, meth)
                new_meth = getattr(klass, new_meth_name)

                # for each of the given values, create a new method which will call the given method
                # with only a value at the time
                for multv in mult_values:
                    def f(self, multv=multv):
                        return new_meth(self, **{param_name: multv})

                    meth_mult_name = meth_name + "_" + multv.replace(" ", "_")[:30]
                    assert not hasattr(klass, meth_mult_name)
                    setattr(klass, meth_mult_name, f)

        return klass

The basics are: it receives a class, it returns a slightly modified class ;). For each of the methods that starts with "test_", I checked those that had two args (not only 'self'), and that the second argument were named.

So, it would actually get the method defined in the following structure and leave the rest alone:

    @test_multiplier
    class SuperTestCase(unittest.TestCase):

        def test_all_scopes(self, scope=_all_scopes):
            resp = self.checker.hit_search(scope, '')
            self.assertTrue(resp)

For that kind of method, the decorator will move it to something not named "test_*" (so we can call it but it won't be called by automatic test infrastructure), and then create, for each value in the "_scopes" there, a method (with a particular name which doesn't really matter, but needs to be different and is nice to be informative to the user) that calls the original method, passing "scope" with the particular value.

So, for example, let's say that _all_scopes is ['foo', 'bar']. Then, the decorator will rename test_all_scopes to _multiplied_test_all_scopes, and then create two new methods like this::

    def test_all_scopes_foo(self, multv='foo'):
        return self._multiplied_test_all_scopes(scope=multv)

    def test_all_scopes_foo(self, multv='bar'):
        return self._multiplied_test_all_scopes(scope=multv)

The final effect is that the test infrastructure (internally and externally) finds those two methods (not the original one), and calls them. Each one individually, informing progress individually, the user being able to execute them individually, etc.

So, at the end, all gain, no loss, and a fun little piece of Python code :)


Novedades pythónicas: fades, CDPedia, Django y curso

Python — Jueves 05 de Marzo de 2015, 23:11


Algunas, varias y sueltas.

A nivel de proyectos, le estuvimos metiendo bastante con Nico a fades. La verdad es que la versión 2 que sacamos la semana pasada está piolísima... si usás virtualenvs, no dejes de pegarle una mirada.

Otro proyecto con el que estuve es CDPedia... la parte de internacionalización está bastante potable, y eso también me llevó a renovar la página principal que te muestra cuando la abrís, así que puse a tirar una nueva versión de la de español, y luego seguirá una de portugués (¡cada imagen tarda como una semana!).

Hace un rato subí a la página de tutoriales de Python Argentina el Tutorial de Django en español (¡gracias Matías Bordese por el material!). Este tuto antes estaba en un dominio que ahora venció, y nos pareció interesante que esté todo en el mismo lugar, facilita que la gente lo encuentre.

Finalmente, empecé a organizar mi Segundo Curso Abierto de Python. Esta vez lo quiero hacer por la zona de Palermo, o alrededores (la vez pasada fue en microcentro). Todavía no tengo reservado un lugar, y menos fechas establecidas, pero el formato va a ser similar al anterior. Con respecto al sitio, si alguien conoce un buen lugar para alquilar "aulas", me avisa, :)


Logging levels

Python — Martes 09 de Diciembre de 2014, 23:15


Cuando empecé con el concepto de loguear, me parecía demasiado tener niveles. Con el tiempo y la experiencia me di cuenta que son imprescindibles, :)

En la biblioteca estándar de Python hay un módulo logging que trae varios niveles prefijados. Son estos, con una pequeña anotación de cómo los uso, más un ejemplo de la vida real (tomados de mi programa de Encuentro o de fades).

- CRITICAL: creo que nunca lo usé :)

- ERROR: problemas de todo tipo; cosas que no deberían pasar, y si pasan son un inconveniente; muchas veces el programa no continúa, o continua de forma parcial o limitada, luego de este tipo de linea logueada. En este ejemplo logueo que no se pudo bajar la lista de los backends durante una actualización (también en este caso se le avisa al usuario mediante una ventanita, y el programa sigue, aunque la actualización no se concretó):

    try:
        _, backends_file = yield utils.download(BACKENDS_URL)
    except Exception, e:
        logger.error("Problem when downloading backends: %s", e)
        tell_user("Hubo un PROBLEMA al bajar la lista de backends:", e)
        return

- WARNING: para indicar que sucedió algo que en general no debería pasar; en general no son cosas malas, sino más bien anómalas, y no presentan una situación problemática. En el siguiente ejemplo estoy dejando registro que ignoro la opción 'quiet' que pasó el usuario (porque también pasó la opción 'verbose', que es más importante):

    if verbose and quiet:
        l.warning("Overriding 'quiet' option ('verbose' also requested)")

- INFO: información general del funcionamiento del programa, cosas que son imprescindibles saber y que siempre queremos que sean registradas; en general no involucran gran cantidad de lineas, pero permite seguir el flujo de ejecución del programa desde un nivel alto. Normalmente los programas que se entregan a los usuarios o corren en los servidores están configurados para realmente mandar a disco desde este nivel. En las siguientes dos lineas muestro lo primero que loguea Encuentro al arrancar: con qué versión de Python está siendo ejecutado y qué versión de sí mismo es:

    log.info("Running Python %s on %r", sys.version_info, sys.platform)
    log.info("Encuentro version: %r", version)

- DEBUG: toda la información necesaria para analizar en detalle la ejecución del programa. Puede involucrar grandes cantidades de información, y hasta ser un problema con respecto al uso de disco o afectar la performance, pero en general no se corren los programas en este nivel, sólo durante el desarrollo o en casos de tratar de analizar un problema específico. No es raro, por ejemplo, pedirle al usuario que ejecute el programa con un parámetro especial que configura los logs en este nivel y que trate de reproducir el problema que tuvo, para luego hacer un análisis forense de la situación. En el siguiente ejemplo estoy dejando constancia que fades tuvo que instalar pip a mano en el virtualenv:

    logger.debug("Installing PIP manually in the virtualenv")

Me ha pasado en sistemas muy complejos de necesitar un nivel más abajo que DEBUG para loguear toda aquella información que podría llegar a ser útil para un análisis del comportamiento del programa, pero que normalmente sería un exceso de datos (lo cual complica desde la lectura de los registros hasta el mismo manejo de los archivos). Entonces, usábamos un nivel TRACE, que casi nunca se prendía, para este propósito.

La macana es que el módulo de logging no tiene un nivel TRACE, pero lo creábamos a mano:

    TRACE = 5
    logging.addLevelName('TRACE', TRACE)

Fíjense el 5 ese: es que DEBUG es 10, entonces queda "más abajo". Claro, para que funcione todo, teníamos que usar un Logger custom:

    class Logger(logging.Logger):
        """Logger that support our custom levels."""

        def trace(self, msg, *args, **kwargs):
            """log at TRACE level"""
            if self.isEnabledFor(TRACE):
                self._log(TRACE, msg, args, **kwargs)

Para más información sobre la infrastructura de logging de Python y consejos generales sobre qué, cómo, o cuándo dejar registro de lo que sucede, pueden ver mi charla sobre el tema (estos sons los slides, y en algún momento se publicará acá el video de esta misma charla que dí en la PyCon de Rafaela).


PyCon 2014 en Rafaela

Python — Lunes 17 de Noviembre de 2014, 23:08


Acaba de pasar la sexta PyCon Argentina. Como dice el título, se hizo en Rafaela, provincia de Santa Fe.

Fuimos con Nico Demarchi en auto, salimos el miércoles a la tarde y llegamos una y monedas de la mañana, volvimos el domingo durante el día, arrancando a media mañana. Creo que es el límite de lo que haría en auto... más distancia ya iría en micro o avión.

Yo tenía que llegar el miércoles a la noche porque el jueves abría el día de talleres con Introducción a Python (modo charla extendida, ya que tenía dos horas). El jueves dí dos charlas más: Cómo debuguear en Python, y Cómo los logs me salvaron el alma.Y para cerrar (justo antes de los sorteos y foto grupal), le conté a la gente un poco cómo íbamos con el proyecto de armar la Asociación Civil de PyAr.

Mis charlas salieron bien, aunque la de debugging no me gustó del todo como la había dado (pero luego recibí buen feedback). Para el taller de Intro a Python usé por primera vez a Pysenteishon, un software muy copado y piola para ir pasando los slides desde el teléfono (¡gracias Emiliano por hacerlo!). Y para las charlas del jueves estuve por primera vez descalzo dando la presentación (era algo que quería probar desde hace rato, y aproveché que el escenario del auditorio tenía piso de madera).

Dando la charla en patas

También fuí a muchas charlas, había muchas cosas copadas para ver, y creo que me salté uno o dos timeslots nada más en toda la conferencia. Las keynotes estuvieron bien, pero no me entusiasmaron particularmente. Y todo lo fue lugar y organización estuvo genial, la verdad que se pasaron. Lo mismo con la gente con la que me (re)encontré: es un placer ser parte de una comunidad así.

Yo llevé la cámara... pero la verdad es que colgué sacando fotos. Pero la grosa de Yami sacó un montón, están todas acá. Y una de las últimas que sacó es justamente la grupal, esta que muestro acá:

Foto grupal

Y como siempre que uno no viaja durmiendo o solo, está el efecto de "PyCon extendida". Es que uno viene charlando de mil cosas, de lo más variado, pero también de proyectos, ideas, etc. Con Nico nos venía rondando en la cabeza una idea para facilitar el uso de dependencias en programas Python, estuvimos charlando con gente en la conferencia, nos dieron feedback, la idea fue mutando... y en el viaje de vuelta se nos terminó de ocurrir algo piola, que no debería ser demasiado loco de implementar; ya les traeré la novedad.

¡Pero no sólo un proyecto me traje! (como si tuviera pocos y/o mucho tiempo libre, ¿no?). Tengo ganas de hacer una "maquinita de timelapse" con una Raspi (una cajita que uno puede colgar en cualquier lado y dejarla ahí algunas horas o un par de días y arme un video de esos donde se ve todo rápido, por ejemplo este). El otro proyecto es armar una valija o caja robusta con todo lo necesario en un PyCamp (router, computadora para caché de repositorios, energía, y varios etcéteras), de manera de tener todo listo y de fácil armado, onda llegás y enchufás. Ya veremos cómo se van desarrollando ambos proyectos...


Satélites argentinos

Python — Miércoles 25 de Junio de 2014, 23:57


Estos días fue lanzado exitosamente el tercer nanosatélite argentino, "Tita" (llamado así en honor a Tita Merello).

Se llaman "nanosatélites" porque, justamente, son mucho más chicos (y baratos) que los satélites "tradicionales". En particular, Tita pesa unos 25 kilos, está equipado con tres antenas y lleva una cámara para tomar fotos y videos en alta definición.

El satélite Tita, siendo instalado en el lanzador

Lo desarrolló la empresa argentina Satellogic, pero no lo lanzamos nosotros al espacio (todavía no tenemos esa capacidad), sino que fue lanzado desde la ciudad rusa de Yasny. Su objetivo es tomar imágenes durante tres años, en colaboración con otros nanosatélites, los ya lanzados Capitán Beto (llamado así obviamente en referencia a la canción de Spinetta) y Manolito (por el personaje de Mafalda), y a otros 15 satélites que Satellogic planea lanzar durante el próximo año.

Pero Tita es diferente a los dos anteriores, que pesaban alrededor de dos kilos. También es un prototipo, y usa las mismas estrategias de diseño y fabricación con componentes de uso comercial (resortes de ferretería, electrónica de teléfonos celulares y computadoras personales), pero este permite tomar imágenes y videos de dos metros de resolución. Esencialmente, la gente de Satellogic está haciendo lo mismo que hace un satélite convencional, pero a un precio entre cien y mil veces menor.

En este video bastante interesante podemos ver a Lino Barañao (Ministro de Ciencia y Tecnología) y Emiliano Kargieman (CEO de Satellogic), contándonos un poco todo esto (y de paso se ven pasos de la construcción, y las oficinas, ¡donde se ve bastante gente de PyAr trabajando!).



Como detalle final, les dejo este audio de Adrián Paenza hablando sobre los satélites (en general) en el programa La Mañana de Victor Hugo Morales.


Recuperando terreno con el cine

Python — Domingo 27 de Abril de 2014, 23:53


Entre los viajes y las vacaciones, estos meses terminé viendo un montonazo de películas. Encima, no aparecieron muchas peliculas copadas para anotar a futuro.

Por otro lado, no estuve viendo muchas series. Con Moni estamos viendo Battlestar Galactica, y yo tengo varias a punto de arrancar (Black Mirror, Almost Human, Through The Wormhole S03).

Pero, a nivel de películas, sí recuperé bastante terreno, :)

  • Chronicle: -0. Muy bien desarrollado el tema de cómo llevar adelante, explorar, y en algún punto sobrellevar, un superpoder adquirido. El resto de la película no vale.
  • Contagion: -0. Muestra de forma interesante (y ajustado a la realidad, me parece) el proceso social ante una epidemia, y las actuaciones están bien, pero le falta como película, como historia contada, como relato.
  • Dream house: +0. Predecible, predecible, predeci..WHAT? Un giro loco, la historia está buena, las actuaciones también; quiere ser un toque de terror pero blah.
  • El hombre de al lado: -0. Tiene partes interesantes, y Daniel Araoz está genial, pero la historia no llega a evitar el naufragio.
  • Elefante blanco: +0. Una realidad que uno (yo) no conoce; bien crudo como acostumbra Trapero. Darín está bien como siempre. Podría estar mejor la historia.
  • Ender's game: +1. Es una buena adaptación del libro, y la película está buenísima. Sí, el libro está mejor, tiene toda una parte que en la peli ni aparece, y es mucho más profundo... pero todo eso no hace que la peli en sí deje de estar buena.
  • Habitación en Roma: +1. Una película hermosa, cruda, y maravillosa, sobre el "enamoramiento".
  • Haywire: -0. Una película de acción que tiene algunos buenos actores un poco desaprovechados, tiene partes buenas, pero meh, es una más sin nada que la haga valer específicamente.
  • Killer Elite: -0. Al final no es más que una historia (que sí está buena) donde muchos machotes están todo el tiempo midiendo a ver quien tiene la pistola más larga.
  • Margin call: +1. Impecablemente contado la interna humana de un descarrilamiento financiero. Me gusta mucho el punto de vista del trabajador interno de la empresa, me pareció muy veraz. Muy buenas actuaciones.
  • Men in black III: +0. Divertida. Más de lo mismo pero con lo interesante de los saltos temporales y mostrar como era MIB en el pasado :)
  • Mission: Impossible - Ghost protocol: +0. No deja de ser la misma película fantasiosa de siempre, pero esta vez me divertí bastante al verla.
  • Monsters University: +1. Tan buena como la primera, aunque totalmente distinta.
  • No strings attached: -0. Natalie Portman no la llega a rescatar; el tema es trillado, no le dan un giro interesante, y Kutcher, como siempre, resta.
  • Paul: +0. Comedia liviana, nada espectacular, para reirse un rato y disfrutar todas las referencias extraterrestroides.
  • The Avengers: +0. Un poco demasiado violenta, pero en el límite (me hacía acordar a Transformers). Me divirtió. Me gustó los (escasos) planteos filosóficos que tiene, aunque al final siempre el mensaje de "menos mal que tenemos superheroes que nos van a salvar cuando todo esté mal", con el que estoy totalmente opuesto.
  • The King's speech: +1. Fantástica película, con actuaciones soberbias, y una historia muy interesante sobre superación personal.
  • The Ledge: +1. La historia interesante, las actuaciones bien. Muy buenos contrapuntos sobre "la religión". Emotiva. Patrick Wilson mejor de lo que esperaba, y Terrence Howard, como siempre, muy muy bien.
  • The divide: +1. Muy bien hecha. Muestra tan bien las miserias humanas que, aunque no soy impresionable y me banco (casi) cualquier cosa, no la voy a volver a ver.
  • The hobbit: The desolation of Smaug: +1. Segunda parte de la trilogía, sigue estando muy buena. Sorprendente la voz de Smaug (el dragón), ¡es Sherlock! http://www.imdb.com/name/nm1212722/
  • The thing: +1. Es vieja, pero los efectos no están tan mal. Y parece que tiene un montón de lugares comunes... hasta que uno entiende que en esa época no eran comunes! ;)


Las anotadas nuevas:


Finalmente, el conteo de pendientes por fecha:

(Sep-2008)    6
(Ene-2009)   18  12   1   1
(May-2009)   11  10   5
(Oct-2009)   16  15  14
(Mar-2010)   18  18  18  16   4
(Sep-2010)   18  18  18  18  18   9   2   1
(Dic-2010)   13  13  13  12  12  12   5   1
(Abr-2011)   23  23  23  23  23  23  22  17   4
(Ago-2011)       12  12  11  11  11  11  11  11   4
(Ene-2012)           21  21  18  17  17  17  17  11
(Jul-2012)               15  15  15  15  15  15  14
(Nov-2012)                   12  12  11  11  11  11
(Feb-2013)                       19  19  16  15  14
(Jun-2013)                           19  18  16  15
(Sep-2013)                               18  18  18
(Dic-2013)                                   14  14
(Abr-2014)                                        9
Total:      123 121 125 117 113 118 121 125 121 110


PyCamp 2014

Python — Viernes 28 de Marzo de 2014, 18:32


Se fue otro PyCamp. Como siempre, genial. Lo charlaba con Moni, es notable como el formato del evento no decae año a año, ¡siguen siendo bárbaros!

Eso sí, voy a tratar de innovar en lo que es la descripción del mismo, escaparme de hacer una cronología, y orientar más el relato a las situaciones.


Llegando y saliendo

Los viajes bien. Como el año pasado, me quedé hasta "cerrar el evento", y también como el año pasado, luego de vaciar el lugar, entregar la llave y eso, nos quedamos algunos tomando unas cervezas en el bar del lugar, hasta que íbamos partiendo en función del horario de bondi de cada uno.

La diferencia estuvo en la llegada, ya que este año no tuvimos al Joven Ocupado en la Accesibilidad y Conectividad (JOAC), así que tuvimos que armar toda la infraestructura de red sin saber demasiado. Viajé con Nico Demarchi, así que al llegar nos pusimos con eso... y aunque no es rocket science, tampoco es trivial, y estuvimos como tres horas para dejar todo lindo!

Una Antena Sable Laser


Proyectos

El proyecto mío en el que más trabajé fue Encuentro, en parte en esta biblioteca para parsear SWFs que vengo necesitando, pero también porque para este proyecto se anotaron varias personas... ¡y metieron un montón de laburo! Tres branches de Mica Bressan, dos de Nico y uno de Emiliano Dalla Verde Marcozzi, y creo que hay otro más dando vuelta por ahí.

También trabajé en un proyecto nuevo, que arrancó en este PyCamp. Es WeFree, un proyecto para almacenar colaborativamente claves de redes, de manera de hacer que tu computadora o teléfono se conecte automáticamente en todos los lados posibles. Participé todo el primer día, en el diseño general y luego armé la interfaz gráfica para la compu (no toda, pero sí la base, dejando algo usable).

Algo en lo que también trabajé desde cero, pero que no sé si se puede calificar como proyecto, fue algo así como la "búsqueda del testrunner perfecto", que describí en este post. Con la ayuda de Martín Gaitán atacamos como base a nose, y le fuimos agregando plugins y probándolos. El experimento fue un éxito, logramos todo lo que queríamos, ya voy a poner un post acá explicando bien el detalle.

Hubo un proyecto que llevé pero en el que yo no trabajé, que fue Linkode, pero Seba Alvarez estuvo haciendo cosas copadas con la interfaz, me tiene que mandar el código.

Finalmente, arranqué ayudando a un par de chicos a migrar código a Python 3, pero no hicimos mucho de eso (aunque aprendimos algunos detalles interesantes).

Laburando en Encuentro con Nico, Mica y Emi (que sacó la foto)


Las noches

Sólo tres, porque el último día uno viaja, pero las aprovechamos a full :)... la gente se va a dormir sorprendentemente tarde luego de lo arduo que son los días. Bah, más sorprendente es que muchos al otro día nos levantamos temprano :p

La primer noche jugué a un juego que no conocía, el Munchkin, ¡y gané!. Está bueno el juego, pero es uno de esos que tenés que leer mil cartitas, entonces las primeras diez veces que jugás se hace un poco lento.

El sábado fue la reunión de PyAr, y después charlé con gente y programé algo, no jugué a nada.

La tercer noche fue doble... Munchkin primero (ganó Matías), y luego jugamos al Carrera de Mente. Hacía como 15 años que no jugaba un Carrera de Mente, no me acordaba que fuese tan divertido! Nos reimos mucho.

Carrera de Mente


Notas de color

Este año Alecu no pudo venir... y Diego Sarmentero se le ocurrió la idea de nombrarlo Lider Inspiracional, y mandó a imprimir dos cuadros, uno para tenerlo durante el día, y otro para tenerlo luego de las cenas.

A nivel de "actividades al aire libre", este año volvimos a repetir la caminata del año pasado hasta el río (fuimos un grupito de unos 10), y también hice paddle con Hugo Ruscitti, Emilio Ramirez y Hernán Lozano. ¡Jugamos un montón! Bah, menos de dos horas, pero nos arreglamos para meter dos partidos (cinco sets rápidos).

También hicimos una key signing party, y Juanjo Ciarlante nos charló un poco de seguridad y buenas costumbres.

Todos


Conclusión

Bien simple, lo afirmo una vez más: ¡PyCamp es el mejor evento del año! (todas las fotos acá).


Corriendo tests

Python — Martes 04 de Marzo de 2014, 18:25


En la vida del programador hay una tarea que lleva bastante tiempo, y es la de correr tests, ya sean "unit tests" (pruebas unitarias) o "integration tests" (pruebas donde se hacen interactuar subsistemas entre sí).

Es cierto, no todos los proyectos tienen tests, pero deberían. ¡Y son un vicio! Una vez que los probaste, querés pruebas en todos los proyectos. Pero claro, a los tests hay que correrlos, y hay muchas maneras de hacerlo.

La verdad es que la estructura de los tests es siempre la misma (o casi siempre), obviamente hablando de proyectos en Python, pero la forma de correrlos, y especialmente la forma de presentar los resultados, varía mucho de un corredor de tests a otros.

A lo largo de años he probado distintos, y debo decir que ninguno cumple 100% con lo que a mi me gustaría tener en el test runner ideal. Por otro lado, seguramente alguno (como nosetests, por ejemplo), cumpla gran porcentaje de lo que quiero, es cuestión de lograr lo que falta.

Acá está la listita de las cosas que cumpliría mi test runner soñado. Propuse un proyecto en el PyCamp de este mes para laburar en esto (obviamente no escribir algo desde cero, sino lograr el objetivo con el menor esfuerzo posible).

Le puse un número a cada ítem para que sea más fácil referenciar en cualquier discusión:

01. Debería soportar que le pase un directorio (default a '.') y que descubra todo ahí y para abajo:

        $ <testrunner> project/tests/
        $ <testrunner>

02. Debería soportar que le pase un archivo, y que corra sólo los tests de ese archivo:

        $ <testrunner> project/tests/test_stuff.py

03. Debería soportar que le pase "paths de import de Python", y que corra sólo tests de ese paquete, módulo, clase, o lo que corresponda:

        $ <testrunner> project.tests
        $ <testrunner> project.tests.test_stuff
        $ <testrunner> project.tests.test_stuff.StuffTestCase
        $ <testrunner> project.tests.test_stuff.StuffTestCase.test_feature

04. Debería poder pasarle una regex para que corra sólo lo que re.search() encuentra en el path completo del método:

        $ <testrunner> project/tests/ --search feature
            correría:
                test_feature
                test_feature_1
                test_feature_2
            no correría:
                test_crash

        $ <testrunner> project/tests/ --search feature$
            correría:
                test_feature
            no correría:
                test_feature_1
                test_feature_2
                test_crash

05. Debería poder decirle que pare de correr los tests al encontrar el primer error o falla.

06. Debería poder indicarle que mida los tiempos de cada test (y al final que presente un reporte con los N tests que más tardaron).

07. Debería mostrar los resultados usando los nombres de paquete/módulo/clase/método, en una jerarquía de árbol o en la misma linea:

        $ <testrunner> project/tests/test_stuff.py
        project.tests.test_stuff
          StuffTestCase
            test_feature_1                        OK
            test_feature_2                      FAIL
          OtherStuffTestCase
            test_feature_A                        OK

        $ <testrunner> project/tests/test_stuff.py
        OK    project.tests.test_stuff.StuffTestCase.test_feature_1
        FAIL  project.tests.test_stuff.StuffTestCase.test_feature_2
        OK    project.tests.test_stuff.OtherStuffTestCase.test_feature_A

    De cualquier manera, esto no afecta el órden de ejecución de las pruebas (secuencial, aleatoria, etc), sólo es cómo mostrar los resultados.

08. Los OKs deberían ser verdes; ERRORs y FAILs deberían ser rojos.

09. Los OKs/FAILs/ERRORs para cada prueba, en el listado, deberían estar alineados verticalmente.

10. No debería capturar stdout/stderr.

11. En el reporte final (luego del listado que va mostrando al ejecutar todo), debería mostrar el path completo del test que falla (o de los tests que fallan), junto con el (los) errores, de manera que si uno copia y pega ese path, sirva para correr ese único test.


Ya tenemos Encuentro 1.1

Python — Viernes 22 de Noviembre de 2013, 22:46


Como estoy seguro que ya sabrán de memoria, ;), Encuentro es un simple programa que permite buscar, descargar y ver contenido del Canal Encuentro, Paka Paka, BACUA, Educ.ar y otros.

Acabo de liberar la versión 1.1, que principalmente vuelve a permitir que el programa se autentique luego de unos cambios en el backend de Conectate, pero también tiene muchos bugs solucionados a nivel de manejar errores en las descargas, y también mejoras en la interfaz.

¡Es fácil probarlo! Tenemos instaladores para Debian/Ubuntu, para Windows, en Arch sólo hacen yaourt -S encuentro, e incluso, desde cualquier lado, pueden instalarlo desde PyPI (sudo easy_install encuentro) o directamente usando el tarball.

Todos los detalles, como siempre, en la página oficial. ¡Que lo disfruten!


El Circo Errante de Python Argentina

Python — Viernes 15 de Noviembre de 2013, 15:08


Hola!!

Charlando en el viaje a/desde la PyCon de Rosario, surgió la idea de armar un Circo Errante (flying circus, la referencia es obvia) con el objetivo de acercar PyAr a las Universidades de una forma distinta a las actuales.

Hoy por hoy, el principal contacto de PyAr con las Universidades es de gente que arma PyDays en ese ámbito. Esto, normalmente, sucede en las Universidades con carreras afines a la programación. Y el contenido y dinámica de estos PyDays están pensados en función de ese público.

La idea es cambiar eso. Y llegar desde PyAr a Universidades con las cuales normalmente no tenemos mucho contacto. Ejemplo de esto son las carreras de derecho, sociología, matemática, psicología, astronomía, ingenierías no informáticas, etc. Estas son carreras en las que los profesionales podrían hacer un buen uso de Python como herramienta, donde no les interesa tanto  el razonamiento "quiero hacer un programa que haga algo", sino más bien "necesito hacer algo y un programa podría ser una herramienta, aparte del excel y la calculadora que es lo que estoy usando ahora".

Más en detalle, lo que se nos ocurrió es armar una especie de presentación basada en ejemplos de distintas especialidades. O sea, cómo usar Python para solucionar tal problema en astronomía, cómo usarlo para encontrar tal resultado en sociología, o cómo aprovecharlo para mejorar un análisis en ingeniería en alimentos. Una presentación semigenérica, de 30-40 minutos, que sea como la punta de lanza: si les gusta la idea luego se puede mostrar Python más formalmente; en el peor de los casos se quedarán sólo con conocer la herramienta y tener idea de qué se trata, que no está tan mal.  Obviamente, la idea es armar una presentación que todos podamos usar, no que cada uno arme la suya.

Para organizar esto, armé una página en el wiki con (por ahora) tres secciones: una de ejemplos, para ir juntando ejemplos piolas en los cuales basar la presentación, una de ideas para folletería/merchandising (ver más abajo) y otra de gente interesada en dar estas charlas y la "zona de cobertura".

Luego, habría que ir buscando contactos en las Universidades, para poder charlar con alguien que nos dé un aula con un proyector y que organice internamente para que vaya gente a la presentación.

Con respecto a la folletería/merchandising, tenemos que pensar en algo para llevar a estas reuniones: algo que les muestre el uso de la herramienta más allá del lenguaje (ejemplo: no mostrar como se escribe un for, sino lo fácil que es hacer una normalización de un conjunto de datos). Algo que se lleven los asistentes con ellos, para poder verlo más tarde, y tener el contacto con PyAr para poder venir por más info.

¿Qué les parece?


Armando certificados con Inkscape y Python

Python — Lunes 11 de Noviembre de 2013, 16:22


Aparecieron muy cerca en el tiempo dos necesidades muy parecidas: hacer certificados de asistencia para PyCon Argentina 2013, y hacer los certificados para mi curso de Python.

Así nació certg, un generador de certificados, que a la vez es simple, versátil, y útil. El proceso para usarlo es simple:

  1. Armás un .SVG (con Inkscape, por ejemplo), poniendole todo el arte que quieras para que el certificado quede lindo. Pero no lo hacés específico a nadie, sino que dejás escrito variables que se van a reemplazar, como {{nombre}} o {{direccion}} o lo que te guste
  2. Armás un archivo de configuración, donde ponés el nombre del .SVG que creaste, qué variable de las que elegiste es única (se usa luego), y luego una lista de grupos de variables, donde a partir de cada grupo se va a generar un certificado.
  3. Ejecutás el programita en Python, pasándole la configuración que armaste. El programita va a leer el .SVG, y por cada grupo en la config, reemplazar esos valores, y generar un certificado (en PDF) usando el nombre unívoco que indicaste.

La verdad, quedó piola: no es dificil de usar, y es lo suficientemente flexible para las distintas necesidades que tuve. Este es como quedó mi certificado de la PyCon:

Fuí a la PyCon!
El proyecto, con ejemplos y todo, acá.


Powered by LifeType