IronPython pyc

Estuve investigando el supuesto camino de pre-compilar módulos para ver si lograba resolver los problemas de performance que mencioné hace unos días.

Resulta que la punta del iceberg estaba escondida en el pack de ejemplos de IronPython, que se descarga aparte. En ese paquete está, entre otras cosas, el famoso pyc.py, parte de cuyo README nos cuenta:

This sample demonstrates the use of the Python Hosting APIs to compile Python files into .NET executables.  The behavior of the generated files is actually quite similar to that of CPython files with the ".pyc" file extension.

Bueno, ni tanto. El script de ejemplo solo es un programita de consola para compilar en bloque una serie de módulos, y generar un assembly. Básicamente, es una interfaz al nuevo método CompileModules del CLR, y hay una explicación bastante detallada de como funciona todo esto acá.

A menos que me esté perdiendo de algo, sinceramente no entiendo de donde sacan que el funcionamiento de todo esto es "similar al de los archivos CPython de extensión .pyc".

Veamos por que:

  • Explícitamente tengo que compilar los módulos. En CPython, esto es transparente.
  • Explícitamente tengo que indicar cual es el módulo que actuará como "entry point". En CPython, esto no es necesario.
  • Si compilo varios módulos, termino con un único assembly con todo. En CPython, cada .py genera su propio .pyc.
  • Pero lo más importante: Para usar el módulo compilado, tengo que cambiar los fuentes que lo consumen (importan). En CPython, el uso de los .pyc por parte del intérprete es completamente transparente.

El último punto es re-importante, y es la diferencia más grave. En CPython, si tengo un módulo foo, en el archivo foo.py, lo importo así:

import foo

… y resulta que si ese módulo está pre-compilado en foo.pyc, es lo mismo. Para mí no cambia nada. El intérprete agarra el .pyc. Y es más: El intérprete se ocupa de determinar si el .py cambió (es decir, si el .pyc ya no es válido).

En IronPython, al usar la compilación de módulos, y asumiendo que ya me haya tomado el trabajo de generar foo.dll (lo que me están vendiendo como "equivalente a un .pyc", para usar el assembly tengo que hacer esto:

clr.AddReference("foo.dll")
import foo

Y eso tiene varios problemas:

  • El código dejó de ser compatible con CPython (por el uso de clr)
  • Si alguien borra el assembly, el código pincha
  • Si foo.py sufre algún cambio, y nadie lo recompila, sigo levantando la foo.dll con el código viejo, y capaz ni me entero

Todo es más o menos workarroundeable: Podría tener un módulo clr "dummy" que no haga nada en CPython, podría meter el AddReference en un try/catch, podría crearme un modulito genérico que generalice toda esta magia, e incluso se ocupe de verificar si el assembly debe ser recompilado… pero no es el punto. Esto NO es lo mismo que un .pyc.

Anyway, estuve jugando con el esquema. Aplicarlo a algo como Cheetah es muy complejo, porque está compuesto por muchísimos módulos. ¿Cuáles compilo? ¿Todos? ¿Algunos? ¿Genero un solo assembly? ¿Genero un assembly por cada módulo? ¿Cuál es la diferencia? La documentación oficial sobre todo lo que tenga que ver con la compilación estática de scripts es sumamente escasa, por no decir inexistente.

Lamentablemente, no llegué a ningún resultado útil. Si compilo Cheetah, igual sigue tardando una bocha en importarse, y lo que es peor: ¡Después no anda! Se importa aparentemente ok (tardando…), pero luego al usarlo empiezan a producirse errores extraños, inesperados.

También noté que si compilo un programa de consola, tengo problemas por ejemplo con sys.argv. En la versión compilada, pierdo los parámetros (sys.argv es siempre ['']).

Conclusión: El esquema de compilación estática no es transparente, no funciona igual que los .pyc de CPython, es más engorroso de usar, obliga a cambiar el código fuente, no acelera la importación, e introduce side-effects y errores inesperados.

Me parece que el equipo de desarrollo de IronPython debería volver a leer el Zen de Python…

Mientras tanto, este esquema a nosotros no nos sirve. No solo no nos resuelve la performance, sino que nos obliga a armar toda una magia para implementarlo, y encima, después funciona a medias, o directamente no funciona.

Probando IronPython 2

Hace un tiempo vengo haciendo diversas pruebas con IronPython 2, principalmente por sus mejoras en compatibilidad con CPython, y sus mejores opciones de integración con VS (en particular VS 2008).

A partir de aquel experimento que alguna vez comenté, en el laburo decidimos usar Cheetah e IronPython como núcleo de nuestro generador de código. Hace un tiempo había empezado a experimentar con IronPython 2, y me dí contra la pared cuando descubrí que Cheetah (en su momento el único template engine que encontré que andaba con IronPython) ya no funcionaba del todo bien, y que además, era *lentísimo*. Oportunamente reporté esto en CodePlex, pero básicamente parecía haber quedado en el olvido.

Los problemas de Cheetah en el peor de los casos podían salvarse con un par de workarounds, pero implicaba tocar bastante código de nuestro lado, y además caer en cosas no recomendadas por la doc de Cheetah. Así que no era lindo. Pero además, lo de la performance era… intolerable. Simplemente intolerable.

Así que seguimos usando IPy 1.1.

En los últimos días me hice de algo de tiempo para probar IPy 2.0.1, la última release estable de IronPython. Y me encontré con 2 gratas sorpresas:

  • Cheetah funciona otra vez as expected;
  • parte de los problemas de performance están resueltos;

Eso último, parcialmente: Lo que encontré es que usando IPy 2.0.1, el generador de código funciona perfecto, y descontando la inicialización de Cheetah, incluso es por lo menos un 30% más rápido que IPy 1.1. ¿Lo malo? La inicialización de Cheetah es PATETICAMENTE lenta.

¿A qué le llamo "inicializar Cheetah"? A un simple

from Cheetah.Template import Template

Eso en CPython es instantáneo. En IPy 1.1 tarda segundos (más de 5", y a veces hasta 10"), y en IPy 2.0.1 tarda el triple que en IPy 1.1 Frown

¿Es un problema de Cheetah? No. Es un issue conocido en IronPython que los module imports son lentísimos, y que a veces muchas de las mejoras de performance de IronPython sobre CPython a nivel runtime se pierden porque de pronto en medio de tu código tenés un inocente import que se lleva el 80% del tiempo de ejecución. Una reverenda cagada. Mal.

IPy 1.1 tenía una opción SaveAssemblies que permitía guardar las DLL que generaba el intérprete. Eso ayudaba un poco, aunque no mucho en el caso de los imports. El tema es que esa opción no existe más en IPy 2.x. Supuestamente, para IPy 2.x se estuvo trabajando en una precompilación similar a la de CPython (algo parecido a la generación de .pyc), y por más que el tema está mencionado en las release notes y todo, no logro encontrar como cuerno se activa y/o usa y/o implementa eso. De todos modos no tengo muchas esperanzas, porque leí en varios lugares que de nuevo, en el caso de los imports, esta precompilación no aporta mucho, pero en nuestro caso algo es algo, y me gustaría probarlo.

Dear lazy web, does anybody know how to test the "precompile" feature of IronPython 2.x?

 

8JRSL

Y así pasaron las 8vas. Jornadas Regionales de Software Libre.

Para mi fueron las primeras, gracias a que fueron en Buenos Aires, y las viví como una "CaFeConf on steroids" :p
Lamentablemente son muchos días hábiles (3), y es complicado coordinar en el laburo la oportunidad de ir todos los días, quizás no por el contenido (al fin y al cabo, en el trabajo varias de nuestras core-tools son software libre), pero sí por el tiempo. Es muy difícil "desaparecer" 3 días. En años anteriores, a la complejidad extra de que las jornadas eran en alguna otra provincia, se sumó que coincidieron con la implementación de algún proyecto. Este año, estamos teniendo (¡por suerte!) un mediados-de-agosto tranquilo, y pude organizar las cosas para asisitir a las charlas que más me interesaban.

También mi participación esta vez fue meramente como expectador (comparando con CaFeConf 2006 y 2007). No es que otros años haya organizado nada… pero al menos, participaba más activamente del stand de PyAr, y de la presencia de PyAr en el evento, al menos aportando ideas. Esta vez, ni eso. Quizás tiene un poco que ver con que PyAr ya lleva varios años participando en estas cosas… y todo sale más de taquito.

Anyway, hay ciertas cosas que deberíamos aprender a distribuir mejor. Por ejemplo, para el pobre de Facu, la "carga" de ser una de las estrellas de PyAr, una de las personas más consultadas, organizar y dar dos charlas, asistir a Raymond Hettinger, coordinar afuera ayuda de la PSF, conseguir libros de O’Reilly para sortear, organizar el sorteo, organizar el concurso de diseño, proceso de selección, confección, y posterior venta de las remeras, y darse el lujo de además colaborar con la organización general de las 8JRSL… es *MUCHO*. Ojo, no dudo que lo disfruta, lo hace por que le gusta, y que mucha otra gente colabora con él en muchas cosas… pero el punto es que PyAr creció mucho, sigue creciendo, y no se… me queda una crítica mínimo a mi mismo por no involucrarme más (de hecho al contrario, estoy menos involucrado de lo que estaba hace un par de años), y tomar la posta para por ejemplo dar una mano con las remeras, que consume un montón de tiempo. Si bien hay cosas en las que es más complicado colaborar (ejemplo, lo de O’Reilly es mucho mejor / más fácil si lo coordina Facu como mimbro de la PSF, que si lo hace un "don nadie"), hay muchas otras cosas para hacer.

El stand de PyAr funcionó re-bien (¡como siempre!), y también se repitió la historia de que en general, los stands de "la comunidad" funcionan mucho mejor que los formales y aburridos stands de las empresas que van solo a venderse.

De las charlas que fui, me quedo con las de Raymond Hettinger; aprendí *un montón* de cositas interesantes de Python. En particular, el tutorial sobre descriptores fue bastante mind-blowing para mí, y terminó gustándome más que la charla sobre core containers.

¿Qué más? Tuvimos nuestra reunión de PyAr, la 31… creo, con record absoluto de gente, de la cual tengo pendiente redactar la minuta. Gracias a toda la gente que vino a Capital Federeal para participar de las jornadas, fue también una reunión realmente "federal", con presencia de gente de varios lugares.

¡Me gané un libro! ¡Sí! Nunca me gano NADA en ningún sorteo… así que fue completamente inesperado. Y qué libro… Programming Python, de Mark Lutz. Unas 1500 páginas. Una Biblia. Gracias PyAr, gracias O’Reilly, y gracias Facu por organizarlo.

No saqué fotos. Eventualmente iré posteando links a las fotos que publiquen otros miembros de PyAr.

IronPython con baterías incluídas

¡Bien! La beta4 de IronPython 2.0 va a incluir la librería estándar de Python. En el laburo estamos usando bastante IronPython para algunas herramientas internas. Y siempre me molestó que por un lado hacían un laburo realmente MUY BUENO en brindar un runtime de Python perfectamente integrado con .NET, y por el otro… le sacaban las baterías (para los no iniciados: solemos decir que "Python viene con las baterías incluídas", por lo amplia, útil y poderosa que es la librería estándar que uno obtiene out-of-the-box cuando lo instala).

La realidad es que no van a incluir TODA la stdlib; hay cosas que hoy por hoy ya se sabe que no funcionan (mayormente, módulos que en todo o en parte están implementados en C, y para los cuales no se hizo ningún wrapper "managed" del lado de .NET). Pero es algo.

Otro punto positivo: IronPython 2.0 va a salir con una licencia dual: La licencia MS-PL de Microsoft, que cubre IronPython, y la Python Software Foundation License, cubriendo la stdlib. Esto es todo un hito: MS acepta, y reconoce, la licencia de la PSF. Según comentan en otro blog, hasta hubo abogados de por medio para poder dar este paso. Bien por la PSF, bien por el software libre, y bien MS.

IronPython 2.0 va a ser compatible con Python 2.5, lo cual agregará varios chiches por sobre 1.1, y probablemente habilitará a que funcionen algunos módulos de terceros que hoy por hoy no funcionan.

Aún tengo pendiente probar todo esto; en particular, quiero aprovechar estas últimas betas para verificar que nuestras herramientas funcionen correctamente con el nuevo runtime.

(gracias Facu por pasarme la data)

 

Creando playlists para el Nokia 6131 desde Linux

El reproductor de música del Nokia 6131 es bastante pedorro. Básicamente porque no ofrece una buena interfaz para navegar la colección de música. Por practicidad, trato de replicar en la MicroSD del celu la misma estructura de archivos que tengo en la PC y en la laptop, a saber:

/Jukebox/Jukebox/Artista 1//Jukebox/Artista 1/Album 1//Jukebox/Artista 1/Album 2//Jukebox/Artista 2//Jukebox/Artista 2/Album 1/

… etc.

Con ese esquema, si le digo al reproductor que tome como carpeta base a /Jukebox, me "aplana" toda la colección en una lista, ordenada como puede. Donde el como puede depende de la metadata de los mp3, y del orden en que se hayan copiado los archivos en la MicroSD. Particularmente esto último, hace que la lista sea cualquiera, especialmente si uno arrastra N carpetas con archivos de una al teléfono, porque el orden en el que se escriben en el filesystem no está garantizado.

Entonces la opción cuando quiero escuchar el album Foo del artista Bar, es reconfigurar el reproductor para que tome solo esa carpeta. Pero esto tampoco está bueno, ni es óptimo, porque:

  • Es lento navegar hasta la carpeta para seleccionarla;
  • Es lento el re-escaneo de los temas, particularmente si hay muchos archivos en la MicroSD (y como es de 1GB, normalmente hay muchos…)
  • No me soluciona el problema del orden. La lista me queda circunscripta a un artista/album, pero si los archivos que conforman el álbum no están en el orden correcto… alpiste.

¿Entonces? De acuerdo al manual del teléfono, y a las opciones que da el reproductor en los menúes, todo parecía indicar que había *algún* tipo de soporte para playlists. Pero por más que intentaba crearlas, no encontraba como. Googleando un poco, encontré que sí, que hay soporte de playlists, pero que no se pueden crear desde el teléfono. Son las clásicas M3U, así que son fáciles de escribir, pero el teléfono no te deja ponerlas en cualquier lado (bah, dejar te deja, pero el reproductor de música no las reconoce). Tienen que estar en una carpeta especial semi-oculta en la memoria del teléfono… y solo las podés crear con el Nokia PC Suite, que es un software muy bueno y muy completo, pero del cual no hay una versión para Linux. Ufa.

Googleando un poco más, terminé dando con varios posts de gente que había encontrado donde se guardan las playlists, y daba pistas de como copiarlas al teléfono con gnokii o gammu.

Resulta que una vez que juntás todas las piezas, no es tan complicado. Por ejemplo, con gnokii:

  • La memoria del teléfono se accede como drive A:
  • Las playlists se guardan en A:\predefgallery\predefplaylist\
  • La microSD es el drive E: [1]
  • Además, las playlist (archivos .m3u) tienen que estar en formato DOS (terminación de línea de DOS)

En general, el soporte out-of-the-box de mi Nokia 6131 en Linux es bastante decente. Cosas como conectarse via bluetooth, o con el cable USB en modo "transferencia de datos", enviar y recibir archivos, están perfectamente integradas y soportadas. Subir música al teléfono es conectarlo (vía bluetooth o con el cable), y hacer drag&drop. Pero Nokia con esto de obligar a que la playlist esté en la memoria del teléfono, y siguiendo sus convenciones absurdas me caga, porque para acceder a la memoria del teléfono, hay que caer en cosas como gnokii, que es software hecho por geeks para geeks, y no es muy amigable ni para configurar ni para usar. :(

Pero una vez configurado, y conociendo dónde y cómo hay que guardar las listas, es relativamente sencillo, y hasta no debería ser tan complejo escribir una linda GUI que permita seleccionar archivos en la tarjeta de memoria, crear una .m3u para los mismos, y subir la lista a su cornudo directorio-secreto-oh-nokia-eres-tan-brillante.

Hasta que tenga tiempo de encarar la GUI, por ahora me armé un script en python que a través de gnokii lista un directorio en particular de la MicroSD, y escupe por stdout la lista de temas que encuentra en ese directorio en formato m3u, y ordenada por nombre de archivo.

Ejemplo:

$ nokia_m3u.py  "E:/Jukebox/Mikael Bolyos/A Family Affair" \> "A Family Affair.m3u"

Y después tengo que subir la playlist a su directorio especial:

$ gnokii --putfile "A Family Affair.m3u" \"A:/predefgallery/predefplaylist/A Family Affair.m3u"

Es bastante directo, pero si escribiera una GUI para arrastrar los archivos y grabar la .m3u en el teléfono directamente desde ahí, estaría mucho mejor. Veremos si invierto tiempo en eso…

[1] Jugando un poco más con gnokii descubrí que la microSD también puede accederse como drive B: (parecerían ser sinónimos), pero como Nokia PC Suite crea las .m3u usando E:… decidí respetar eso. Y la memoria del teléfono también es visible como drive C:

 

Ubuntu 8.04

Terminé la migración de la compu y la laptop a Ubuntu 8.04. Esta es la segunda versión LTS, o Long Time Support. Inicialmente iba a esperar algunos días más para migrar, pero hoy tenía tiempo, estaba aburrido, y venía probando los últimos dos betas y el RC sin mayores problemas, así que me animé.

El mayor miedo era que dejara de andar la placa wi-fi de la laptop, ya que en los últimos kernels de Linux se viene trabajando en un refactoring de código importante de la infraestructura de soporte para los drivers de dispositivos WLAN, que entre otras cosas, obligó a re-escribir el driver de mi placa (una Intel 3945)… que paradójicamente era uno de los más maduros, estables, fáciles de hacer andar. Y su reemplazo tenía un par de bugs que se arreglaron apenas después de la beta6. De hecho en lo que a mi me concierne sigue habiendo un bugsito que finalmente no se arregló, y es que con el nuevo driver no funciona el led que indica si la placa wireless está activada o no.

Volvamos a la actualización: En ambas máquinas el upgrade funcionó sin novedades. Al igual que las últimas veces, realicé el upgrade con el CD de instalación "Alternativo", de manera de tener la mayoría de los paquetes disponibles en CD. Eso acelera mucho el proceso, y lo hace más inmune a mirrors lentos… que estos días estaban que ardían con todo el mundo upgradeando. La PC (que actualicé primero), salió andando de una, sin ningún problema. Supongo que ayuda que hoy se transformó especialmente en un server y máquina de respaldo, y he desinstalado parva de cosas, con lo cual hoy por hoy es una configuración sumamente minimalista y estándar.

Con la laptop tuve un problema complicadito de resolver, culpa de esas cosas que es imposible probar con el LiveCD, y que hasta que no hacés la instalación posta no te das cuenta que no andan. Y culpa también de una configuración bastante particular de mi parte. Resulta que tengo mi HOME encriptado. Esto es más que nada para que si me chorean o pierdo la laptop, estar medianamente tranquilo que no desparramo por ahí un montón de info personal mía, y de amigos y familiares. Pero el approach que usé para configurar la encriptación no es precisamente el más robusto (después de todo, la idea no es proteger la info frente a un técnico de un servicio de inteligencia, sino ante un choro cabeza de mierda de los que abundan últimamente…), sino el más fácil de configurar. Estoy usando EncFS, que es un layer de encripción liviano que corre en user-space por arriba de FUSE. Esto está pensado más que nada para encriptar directorios y/o archivos individuales, pero yo tengo encriptado todo mi HOME. Y tengo instalada una magia que a través de PAM me da acceso a la info encriptada al iniciar cesión, para que sea "transparente", y no tener que estar ingresando múltiples passwords. Y resulta que la versión de libpam-encfs con la que salió Ubuntu 8.04 está compilada mal, y no se puede cargar.

El efecto fue que al reiniciar la máquina por primera vez luego de la actualización, y obviamente sin saber nada de todo esto, me encontré con que ni bien ponía mi nombre de usuario para iniciar cesión, me saltaba un alerta de "Authentication failed" (sin siquiera pedir el password). Y no había forma de entrar. Con ningun usuario. Ni desde el session manager ni desde una consola. Googleando un poco encontré que el bug estaba reportado desde hacía ya un tiempito, que es una boludez (un flag de compilación!), que está detectado, y calculo que en los próximos días saldrá un rebuild oficial del paquete con el fix. Pero mientras tanto… tuve que encontrar todo esto, bajar el source del paquete, el patch que incluyen en el bug report, y rearmar el paquete aplicando ese patch, y reinstalarlo. Todo esto desde una cesión "chrooteada" booteando con el LiveCD. Una vez detectado el problema fue fácil, pero ciertamente son esas cosas que uno resuelve por experiencia en la plataforma, mientras que un usuario final no sabría para donde correr. Aunque también es cierto que un usuario normal no tendría el setup pseudo-complicado que tengo yo (de hecho, el reporte de este bug pasó completamente inadvertido para mí, a pesar de que hacía varias semanas que venía prestando atención a posibles problemas que podrían presentarse en un upgrade, y supongo que fue así porque no es un bug que haya mordido a demasiada gente).

Sorteado ese susto… pude empezar a disfrutar la nueva versión de Ubuntu. No voy a hablar de cosas nuevas en detalle, porque hay miles de reportes y blogs y servicios de noticias publicando posts con las novedades de Ubuntu 8.04 desde hace meses. Pero si quería detenerme un poquito en PulseAudio, el nuevo sound server. Instalando un par de aplicaciones de configuración, pude probar los dos features más interesantes:

  • Controlar en forma individual el volumen de cada cosa que está sonando;
  • Publicar en la LAN las placas de sonido, de manera de poder hacer cosas como reproducir música en la laptop, pero escucharla por el hometheatre conectado a la PC :)

Y lo más lindo es que todo esto se configura con 2 o 3 clics en las herramientas correspondientes, y Just Works. Y es dinámico. Los servers se "publican" en la red mediante Avahi, y usan Zeroconf, así que se auto-descubren, sin configurar IPs, ni puertos ni nada. Y en cualquier momento, desde el control de volumen, uno puede "transplantar" un stream de audio de un dispositivo a otro. Impresionante. PulseAudio es multiplataforma, así que encima esto funciona en una red mixta con Linux, Solaris, FreeBSD y Windows (sorry Mac… :p)

Otro cambio muy visible, es Firefox 3 (Ubuntu 8.04 instala la beta5). Para los usuarios de Linux, es realmente bienvenido que los developers de Mozilla se hayan puesto finalmente las pilas, y hayan hecho que Gecko use los widgets nativos de la plataforma en los forms, y que Firefox herede los iconos, colores y demás look&feel de la apariencia general configurada a nivel global. Así que ahora Firefox dejó de parecer un alien. Esta fue una de las principales causas de que dejara de usar Firefox con Gnome, y empezara a usar Epiphany. Además Epiphany es mucho más liviano. El tema es que ahora Firefox3 (teóricamente…) también está mejorado en ese aspecto. Pero a su vez Epiphany usa Gecko, y muchas de las mejoras de Firefox3 son en realidad mejoras de Gecko, con las cuales las tiene también Epiphany. Conclusión: Voy a tener que probarlos bastante más codo a codo para determinar si Epiphany continúa siendo una mejor alternativa (para mí, claro está), o si vuelvo a Firefox.

 

Haciendo que Ubuntu 7.10 se despierte bien

Hacía rato que tenía problemas con Ubuntu en la laptop con el soporte de "Suspend to RAM".

Suspender, suspendía. El problema era al despertar. Pasaban 3 cosas:

  • La pantalla no se encendía. 
  • Me quedaba sin sonido. El control de volumen maestro quedaba silenciado, y además, se bajaba completamente el volumen PCM.
  • Me quedaba sin wifi. Mal. El dispositivo desaparecía, el led de la laptop quedaba apagado y no respondía a la combinación de teclas Fn+F2 para encenderlo, y NetworkManager determinaba que estaba solo en el mundo, sin ningún dispositivo que atender.

Lo de la pantalla tenía un workarround: apretar CTRL-ALT-F1 y luego ALT-F7, o sea, conmutar a modo texto y volver a modo gráfico. Eso terminaba de despertar al video. Feo, pero eran dos golpes de tecla. 

El tema del sonido, también tenía un workarround: Des-silenciar el master, abrir el control de volumen, y volver a subir el volumen PCM. Me hinchaba las pelotas, sí, pero se arreglaba con algunos clics del mouse.

El tema de la red wifi tenía un workarround HORRIBLE: reiniciar :(

Hace unos días me puse a investigar más a fondo. El objetivo primario era hacer que funcione ok (i.e., que al despertar la laptop tuviera red otra vez automáticamente); el premio consuelo que estaba dispuesto a aceptar era encontrar que servicios reiniciar (y en que orden) para que volviera a funcionar, y al menos no caer en reiniciar la máquina (que es la salida típica en Windows, pero NO en Linux). 

Después de googlear bastante, la primera conclusión a la que llegué es que todo "debería" funcionar bien; los problemas que estaba experimentando eran típicos de hace un par de versiones atrás, pero no de Ubuntu 7.10. Si bien no era el único que tenía estos problemas, también era mucha la gente que reportaba que todo le funcionaba perfecto "out of the box".

¿Entonces? Empecé a bucear en decenas de bug reports y threads en foros y demás, probando tocar un archivo u otro (casi siempre, /etc/default/acpi-support), para comentar una línea o descomentar otra, o cambiar cierto parámetro… pero nada. El tema no se arreglabla. Finalmente, terminé encontrando un post en un foro donde un usuario daba la siguiente receta, al iniciar luego de un suspend:

  • parar el servicio de networking;
  • parar NetworkManager;
  • forzar la descarga del módulo del kernel correspondiente a tu placa wifi (en mi caso, ipw3945); 
  • volver a cargar el módulo;
  • iniciar networking;
  • iniciar NetworkManager;

Voilá!. Andaba. Y era suficientemente sencillo como para armar un pequeño script que automatizara eso, cosa que terminé haciendo. Cada vez que la laptop salía de un suspend, corría el script, y volvía a tener red. Además, claro, tenía que seguir despertando "a mano" el video y el sonido. Feo. Pero ya había dado un paso: No reiniciar.

Pero todo esto me dejó pensando… si todo se limitaba a reiniciar servicios, ¿entonces por qué no andaba? ¿Por qué hay N personas a las que no les andaba, y M personas a las que sí? Y entonces empecé a confirmar la sospecha de que al fin y al cabo todo era producto de los upgrades sucesivos de una versión de Ubuntu a la siguiente. Este tema (el power management) hace un año y pico atrás tenía problemas. Ahora estaban resueltos. Pero probablemente algo (vaya uno a saber qué…) no había sido correctamente pisado, reconfigurado o reemplazado en alguno de los upgrades, y por eso me seguía funcionando a medias.

Y decidí probar lo siguiente: La mayoría de las recetas para arreglar estos problemas implicaban modificar el archivo /etc/default/acpi-support. ¿Qué pasaría si eliminara COMPLETAMENTE (incluyendo la configuración) el paquete "dueño" de ese archivo, y lo reinstalara?

Ejecuté un

$ dpkg -S /etc/default/acpi-support

y me enteré que el archivo es parte del paquete acpi-support. Probé desinstalarlo, y APT me dijo "no! no podés desinstalar eso, porque el paquete powermanagement-interface depende de acpi-support". Igual, decidí forzar la desinstalación ignorando las dependencias (total, pretendía reinstalarlo inmediatamente después, con lo cual no iba a quedar nada roto), pero ya que estaba metí en el trámite también a ese otro paquete:

$ sudo dpkg --purge --force-depends acpi-support$ sudo dpkg --purge --force-depends powermanagement-interface

Listo. Con el --purge me aseguraba de haber eliminado también toooooodos los archivos de configuración, y que por lo tanto al reinstalarlos, se configurara todo de cero. A reinstalar:

$ sudo aptitude install acpi-support powermanagement-interface

Y listo! ¿Adivinen qué? Ahora anda todo 100% Ok. La laptop suspende perfecto, y se despierta perfecto, con video, sonido y wifi.

Algunas moralejas de esta historia:

  • Por maravilloso que sea actualizar el sistema operativo "on the fly", de una versión a otra, es muy complicado que el proceso cubra el 100% de los detalles. Y esto se potencia cuando las actualizaciones se acumulan (yo ya voy por el 3er upgrade, y en un par de meses sale Hardy, e iremos por el 4to…)
  • Si algo no te anda, y a otras personas sí, y tu instalación no fue hecha desde cero, empezá a sospechar de que algo haya quedado mal por ese lado.
  • Si empezás a tocar acá y allá, y no obtenés resultados, y a la larga lo que estás buscando es la configuración "por defecto" en la que todo debería andar, empezá a preguntarte si no es mejor purgar completamente el componente de software en cuestión, y reinstalarlo para que se autoconfigure solito.
  • APT (el sistema de paquetes) es una joyita. Es fantástico poder llegar de un archivo a un paquete, poder desinstalarlo COMPLETAMENTE, incluyendo a la configuración, y que la reinstalación se ocupe de auto-configurar todo nuevamente.
  • Linux es fantástico por su modularización, y por permitir estas cosas. Ok, en un mundo ideal esto no debería pasar, y tampoco la solución es para cualquiera (no la veo a mi vieja por ejemplo siguiendo estos pasos), pero una vez ante el problema, prefiero mil veces lidiar con algo así en Linux, al que le puedo preguntar donde le duele (¡y me responde y todo!), que en Windows, donde no queda otra que recurrir a la magia negra… o reinstalar TODO, ABSOLUTAMENTE TODO.

 

Extendiendo Trac

En el trabajo ya hace bastante tiempo estamos usando Trac, con bastante éxito. Tiene sus limitaciones, ya que manejamos muchos proyectos, y sobretodo para la gente que tiene que coordinar más de un proyecto, se vuelve muy tediosa la falta de una "vista consolidada" que permita consultar y administrar al conjunto de proyectos como un todo. Por ejemplo, responder la pregunta "¿cuántos tickets tiene asignados fulano en TODOS los proyectos en los que está?" es complicado.

Esa parte la resolvió Diego exportando cada N tiempo cada una de las bases de datos SQLite de cada proyecto a una base de datos dentro de SQLServer, y ahora se están desarrollando varios reportes usando las herramientas estándares de la consultora. La desventaja es que la información es un snapshot (i.e., no tenemos la info en tiempo real, actualizada al instante), la enorme ventaja es que estamos pudiendo explotar la info de Trac de una manera mucho más rica, a la vez que nos permite una integración mucho más fuerte con nuestras otras herramientas. En cuanto a esta parte del dilema (consolidación de la información), la pata que está faltando es interactuar con Trac, es decir, no solo consultar la info, sino por ejemplo hacer operaciones masivas, del estilo seleccionar N tickets de X proyectos que cumplen tal criteria y cerrarlos. Diego está experimentando con una especie de RPC casero haciendo POSTs a Trac, yo tengo pendiente instalar en un entorno de prueba el XmlRpcPlugin y ver si nos da alguna ventaja.

Otro problema que teníamos era la consistencia entre proyectos en cuanto a "Prioridades", "Tipos de tickets", "Severidades", etc., etc., más cuestiones como definir que componentes por defecto están activos, que usuarios tienen que permisos, etc. Eso lo resolvimos haciendo un wrapper alrededor de TracAdmin. Pero no "trac-admin" el comando, sino TracAdmin a nivel de componente. Este wrapper puede usarse por línea de comandos o como un servicio web (que invocamos por ejemplo desde una página de creación de proyectos), y "sabe" hacer un initenv usando todos nuestros defaults: borra las cosas que no necesitamos, agrega las que sí, setea defaults y permisos, etc. Así cualquiera puede inicializar un nuevo proyecto en Trac con total confianza de no olvidarse ningún paso, y de que va a cumplir con nuestro estándar interno. Y el 99% de las cosas que hace este wrapper están definidas en un archivo externo de configuración, con lo cual es simple modificar / extender lo que hace.

Sacando esos dos grandes temas, aún tenemos pendientes varios detalles mas finitos, varios de los cuales son candidatos a o bien encontrar un plug-in en Trac Hacks que nos de la funcionalidad requerida, o implementar nuestro propio plug-in. Uno de esos temas tiene que ver con que Trac 0.10 no maneja workflows, la versión 0.11 se sigue demorando… y demoraaaaaaando, y estábamos necesitando empezar a validar ciertas cosas, en particular, de consistencia de valores de algunos campos al cerrar un ticket. Así que con un poco de expermientación del finde, más un ratito hoy en el trabajo para afinar detalles, me largué a escribir mi primer plugin para Trac.

Es realmente MUY fácil. La mayor parte del tiempo la invertí investigando de cual de tooooooooodos los puntos de extensión que expone Trac tenía que colgarme para hacer lo que yo quería. Una vez descubierto eso, fue muy sencillo. Quería validar el ticket al grabar cambios. La interfaz a implementar resultó ser ITicketManipulator. Macheteandome un poco en el código SpamFilterPlugin, y leyendo la MUY buena doc de Trac, el resto fue coser y cantar.

No publico el código completo porque está muy pegado a lo que hacemos internamente en el trabajo, pero en esencia, el core del plug-in es algo así:

from trac.core import *from trac.ticket import ITicketManipulator, TicketSystemclass RechazarTicket(TracError):  """Excepcion a generar si el ticket es inválido."""class MiValidator(Component):  implements(ITicketManipulator)  # Métodos de ITicketManipulator  def prepare_ticket(self, req, ticket, fields, actions):    pass  def validate_ticket(self, req, ticket):    if 'preview' in req.args:      # Si es un preview, y no estamos grabando, no validamos nada      return []    if ticket['status'] != 'closed':      if ticket['version'] in ('XXX', 'YYY'):        raise RechazarTicket('La versión %s solo es válida si el ticket está cerrado.' % ticket['version'])      # El ticket aún no está cerrado: No validamos nada más      return []    # Recuperamos todos los campos que NO son de texto    fields = [f['name'] for f in              TicketSystem(self.env).get_ticket_fields()              if f['type'] not in ('textarea', 'text')]    for field in fields:      # Acá implemento las validaciones que quiera...      # "field" tiene el nombre del campo (ej., "version", "status", etc.)      # puedo acceder al valor haciendo      #    ticket[field]      # y puedo ver el valor anterior (para ver si se está modificando) haciendo      #    ticket._old[field]      # Si alguna de mis validaciones custom no se cumple, se hace un      # raise de RechazarTicket... y listo.    return []

Como ven, muy fácil. Ahora entiendo un poco más por qué en Trac Hacks hay TANTAS cosas… es que realmente es simple extender Trac. Ahora que rompimos el hielo con este primer plugin… probablemente se vengan más a futuro.

 

CDC UNLUX 2007 – Conectando Puntos

El sábado se realizó el Ciclo de Charlas UNLUX 2007, "Conectando Puntos", organizado por el Grupo de Usuarios de Software Libre de la Universidad Nacional de Luján (UNLUX para los amigos :p)

Evento que tuvo el "honor" de ser auspiciado por PyAr. ¿Será un honor? Jeje… hablando en serio, el honor fue nuestro, y marca un hito. Esperemos que en adelante podamos seguir auspiciando estos eventos.

Otro punto que hace especial a este ciclo de charlas es que fue generado desde adentro de una universidad, y eso es fantástico. Las universidades tendrían que tener un contacto MUCHO más grande con el software libre del que tienen… por una cuestión de soberanía y autonomía, por una cuestión de flexibilidad a la hora de adaptar el software a sus necesidades, por una cuestión pedagógica, especialmente en carreras de informática, dada por la posibilidad de de ver y modificar el código fuente, y como si todo eso fuera poco, por una cuestión económica.

Este fue el primer ciclo de charlas organizado por el UNLUX, y fue todo un éxito. Mucha más gente de la que esperábamos (el puntapié inicial de estas cosas no siempre tiene la convocatoria que uno quisiera), y la organización estuvo muy prolija. Hasta los chicos tuvieron que capear un corte de luz general de varias horas en toda la universidad.

Hubo una gran cantidad de charlas, de diversos temas. Obviamente, Python estuvo ahí, como corresponde :)

Luego del cierre de las charlas, gran parte de los organizadores, disertantes, un grupo de PyAr y algunos otros asistentes nos fuimos a festejar, cenar y pasar un rato agradable a una pizzería.

Facundo escribió un review bastante detallado del evento en este post.

Por último, si quieren fotos… las mías están en este álbum. Los chicos de UNLUX también publicaron fotos en su site, en el álbum CDC -> 2007 :: Conectando puntos. Y Facundo subió más fotos acá.