el blog de cHagHi

(el rejunte on-line de todo aquello que deseo compartir)

 

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.

 

Y ya que estamos, un año antes escribía: Ubuntu 7.04 - Feisty Fawn :: Flickr, un par de años después :: Libros, libros, libros

El caso del email a 500 millas

Cada tanto, alguien en reddit.com/programming "desentierra" algo interesante, como esta historia.

Tiene unos cuantos años, pero no la conocía. Habiéndome enfrentado muchas veces a bugs raros o problemas de configuración extraños, la disfruté mucho. Y supongo que mis amigos sysadmins ahí fuera le encontrarán aún más jugo :)

 

Y ya que estamos, un año antes escribía: El Conquistador

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.

 

Y ya que estamos, un año antes escribía: Hot Joy 2007 :: Cuenta regresiva para las vacaciones :: El Laberinto del Fauno

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, TicketSystem

class 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.

 

Y ya que estamos, un año antes escribía: CaFeCONF 2006 - Promoviendo Python

Back to Pidgin

Hasta hace un año atrás, mi cliente de IM de preferencia sobre Windows era (al igual que sobre Linux) Pidgin (ok, en aquel momento se llamaba Gaim).

Siempre me gustó pero había especialmente dos cosas que me molestaban: La cantidad de recursos consumidos, si bien sensiblemente menores a la suma de GTalk + MSN Live Messenger juntos, siempre me pareció excesiva para un cliente IM. Y GTK sobre Windows (especialmente sobre Win2K) siempre me pareció un tanto alienígena en cuanto a look&feel y ciertas convenciones de uso.

Entonces decidí probar Miranda IM, por recomendación de un amigo. Miranda apuntaba a resolver justamente mis dos mayores issues con respecto a Pidgin: Por un lado es MUY chiquito. El core de la aplicación es MINIMO, y todo, absolutamente todo lo demás son plug-ins. Por otro, es una aplicación desarrollada específicamente para Windows.

Hoy, un año después, vuelvo a Pidgin. Varias cosas han pasado: Desde hace varios meses tengo más RAM en la PC del laburo, las últimas versiones de Pidgin han mejorado muchos detalles; hoy por hoy consume menos recursos de lo que consumía hace un año (por optimizaciones en Pidgin, y en GTK en general), y GTK sobre Win2K también está más integrado.

Pero por sobretodas las cosas, me hartó la falta de pulido y la inestabilidad de Miranda. La idea es excelente. La arquitectura de la aplicación me encanta. Pero parece una aplicación "by geeks for geeks". Y si bien tengo mi costado geek, a esta altura del siglo XXI cuando quiero un cliente de IM quiero uno que me funcione, y me funcione bien, y no uno al que SIEMPRE tengo que estar "perdonándole" algún detalle: plug-ins que se rompen en cualquier actualización de un día para el otro y por tiempo indefinido, problemas de internacionalización, de notificación, de manejo de unicode, iconos "feuchos", pixelados, y muchas cositas más. Todos detalles. En su mayoría, son temas de pulido, o de poco testing. Pero todos fueron sumando a un descontento generalizado con la aplicación que hoy finalmente hicieron que el vaso rebalse, y volviera al viejo y querido Pidgin.

 

Y ya que estamos, un año antes escribía: Yo recomiendo Ubuntu :: PyAr ya tiene bandera :: RoxBox 86-06

Exoditus en Python

En varios posts anteriores creo haber mencionado (a veces al pasar, y a veces no tanto), al generador de código que usamos en el laburo en los desarrollos .NET, a quien su autor, Darío, denominó Exodus. No, el nombre no es arbitrario... pero en todo caso le tocará a Darío algún día escribir sobre ello en su propio blog... :)

Sin entrar en detalles (je! a ver si avivamos giles y perdemos la ventaja competitiva que nos da!), digamos que es una herramienta mediante la cual se define el "dominio" de una aplicación, generalmente haciendo algo de introspección sobre el modelo de datos (o sea, sobre la base de datos a usar), y genera automáticamente un montón de código boilerplate: el mapeo entre el ORM y la base de datos, "finders" para recuperar datos, consultas y ABMs estándares (al estilo de las que genera por ejemplo Django), las fachadas y business delegates (en el laburo, a pesar de usar .NET, usamos muchos de los patrones arquitectónicos de J2EE) y un sinfín de cosas más.

La herramienta es fantástica, e imprescindible: Creo que sería imposible desarrollar sistemas del tamaño de los que estamos generando, si tuviéramos que escribir todo eso a mano, cada vez (y si cada vez que tocáramos el modelo de datos, tuviéramos que ir manualmente a reajustar TODOS los lugares en los que pega). A pesar de todo, desde el comienzo que hay algo que me "molesta" (muy entre comillas, porque en definitiva, FUNCIONA, y bien), y es que internamente, el core de Exodus no deja de ser un template engine, que toma una descripción del dominio de la aplicación, y una serie de plantillas, y escupe código (o más académicamente hablando, "text artifacts" (¿ahí les gusta más?)). Y el problema es que usa un template engine custom, y peor, ese template engine usa un lenguaje scripting custom, que tiene varias limitaciones (se han tenido que hacer cosas "raras" en las plantillas, y en el código generado, por limitaciones en los tipos de datos que maneja este lenguaje, por ejemplo). Y sobre todo, es LENTO. Esto hasta ahora no era un problema, pero en el proyecto en el que estoy trabajando desde hace varios meses, que es MUY MUY grande, regenerar código lleva más de 10 minutos.

Ninguna de las dos cosas constituyen una limitación "insalvable". De nuevo, funciona, funciona ok, el código no se regenera a cada rato, no todos los proyectos son tan grandes, y cada vez que nos topamos con alguna limitación a nivel de templates... Darío encontró un workarround.

Pero la cuestión es que a mí el tema me seguía dando vueltas en la cabeza... y siempre tenía en la cabeza una vocesita diciendo "que bien que vendría Python y alguno de los tantos templates engines que tiene acá..."  Encima, Exodus desde hace un tiempo atrás tiene embebida una consola de IronPython, que sirve para levantar "on-the-fly" el modelo antes de generar código, y jugar un poquito. Todavía no le hemos dado mucho vuelo... pero es cool :)

Hace unos meses atrás, mejorando el esquema de remoting estándar que usan nuestras aplicaciones, se presentó un problema "recursivo": Ciertos Business Delegates dependían del código escrito por nosotros (no del auto-generado), con lo cual en principio no se podían generar con Exodus... y ahí nació "Exoditus", que no es más que un hook de post-compilación de parte del proyecto que levanta el core de Exodus, levanta el assembly recién compilado con la lógica de negocios, mediante reflection extrae algunas propiedades, genera código adicional, y sigue compilando. Nice.

Este fin de semana, decidí poner manos a la obra, y ver si podía, como experimento y prueba de concepto, re-escribir Exoditus en IronPython. Elegí Exoditus porque es lo más fácil de reemplazar. Hoy por hoy, aunque la idea funcione, reescribir todos los templates y modificar Exodus para que "dialogue" con este esquema ya no es tan trivial (aunque tampoco lo veo como algo muy complejo). En cambio Exoditus bien puede no depender para nada de Exodus; es un paso intermedio en la compilación. Nada más. Y solo UN template.

Bueno, el experimento funcionó. La parte más compleja, fue encontrar un template engine que funcionara BIEN en IronPyhon. Los template engines que hay suelen (todos) hacer uso de mucha magia negra y de features de CPython que aún no funcionan en IronPython. Luego de algunas pruebas, y con la ayuda de Google, el candidato elegido fue Cheetah. Buenísimo. Quizás no sea el ciudadano más ilustre por estos días, con todo el buzz alrededor de Django, TurboGears y cía., y los template engines orientados al desarrollo web. Pero es un producto maduro, estable, en producción, rápido... y MUY poderoso.

Así que prueba superada: pyExoditus se compone de +/-90 líneas de código, de los cuales el 70% se va en dos métodos que realizan introspección sobre un assembly que recibe como parámetro, para extraer nombres de clases y métodos que cumplen ciertos patrones, un template, Cheetah, y unos 32 módulos de la stdlib de Python (me tomé el laburo de identificar uno a uno cuales eran, para poder armar un "paquete" autocontenido con todo lo necesario que no requiera tener CPython instalado para andar). La buena noticia: No solo anda, sino que anda sensiblemente más rápido :)

No se si terminará metido o no en nuestro estándar de desarrollo... pero en cualquier caso, fue divertido y productivo. Y si termina metido, quizás sea el primer paso para apuntar a otro objetivo: Darío está en estos momentos escribiendo la versión 2.0 de Exodus, con muchas mejoras y cambios estructurales... quizás... solo quizás, Exodus 2.0 podría usar como template engine a Cheetah, y como scripting language a Python...

 

Oído al pasar...

Hoy un compañero de trabajo (a quien no pienso deschabar...) me hizo el siguiente comentario por GTalk:

Miré un texto en un papel, hice foco en un textbox, y apreté CTRL-V esperando que pegue... estoy limadísimo

Sí. Muy. Definitivamente.

 

Aprendiendo erlang

Hace un par de semanas atrás, empecé a leer un poco documentación y tutorials de erlang. Instalé en mi laptop la implementación open source, y estuve practicando.

Nada fancy, nada útil per-se. Estoy apenas escarbando la superficie, para ver algo concreto de programación funcional (algo que tenía pendiente hace rato). Y elegí erlang porque dentro de los otros lenguajes funcionales (o pseudo-funcionales) que miré encontré que tiene una sintaxis clara. Y tiene algunos pythonismos:

  • tiene un intérprete interactivo (tener un intérprete interactivo es LO MAS!)
  • tipado dinámico
  • evaluación estricta
  • asignación única (esto para mí era importante, porque realmente quiero probar como es programar SIN depender de cambios de estado para expresar la lógica (creo que es el click-mental más costoso para pasar a hacer algo funcional...))

Por otro lado, si bien es relativamente "viejo" (si se puede decir viejo sobre un lenguaje de programación...), últimamente viene ganando terreno. Quizás porque algunas singularidades son más útiles o fáciles de explotar ahora. Y esas singularidades de erlang también ayudaron a que terminara siendo "el elegido" para esta prueba: El lenguaje incorpora en sí mismo todo lo necesario para programación concurrente, ejecución en paralelo, ejecución distribuida, pasaje de mensajes, rule matching, de una manera sumamente simple y completamente integrada al core del lenguaje.

Hasta ahora me viene resultando un experimento interesante, sobretodo porque acostumbrado a lenguajes procedurales y orientados a objetos, es un desafío a veces expresar las cosas más sencillas cuando el paradigma es funcional.

Con todo esto ni quiero decir que me vaya a dedicar a desarrollar en erlang ni mucho menos... pasa que la "pata" de paradigma funcional me está faltando, y me parece que la mejor forma de incorporarla es jugar un rato con un lenguaje 100% funcional. Después, es más fácil volver a otros lenguajes y aplicar esas "lecciones aprendidas" a la resolución de los problemas, aún en casos en que el lenguaje en sí no soporte el paradigma. Se trata solamente de poder incorporar otro enfoque, otro punto de vista, que a veces es más eficiente / útil.

Y calculo que una vez que tenga más en la cabeza estas cosas a partir de elrang, va a ser más fácil echarle una mirada a otras opciones (como por ejemplo, Haskell) de las que huí despavorido por su horripilante sintaxis (ok, no le puse mucho esfuerzo de mi parte en darle una oportunidad, pero... ¿para qué el masoquismo, teniendo erlang?)

Algunos links que me resultaron útiles al principio:

  • An introduction to erlang: Este está recién salido del horno, pero lo pongo primero porque lo leí ayer y es un excelente paneo de las cosas más significativas

 

Y ya que estamos, un año antes escribía: Ge oss en bild av ditt Stockholm :: One Wish :: ... y seguimos con el cine

ISO votó en contra del reconocimiento de Open XML como estándar internacional

A Microsoft se le viene complicando desde hace rato, sobretodo en Europa, el tema de sus formatos "opacos" de datos para documentos.

Hace varios años atrás, con el respaldo de Oasis, se definió un estándar internacional: OpenDocument.

Mientras tanto, Microsoft diseñó un nuevo formato, basado en XML, que se llama "Open XML". Sí, es XML. Sí, es texto. Sí, el nombre tiene el substring "open", pero muchas personas que analizaron a fondo la especificación notaron que:

  • Es INSANAMENTE grande (más de 6000 páginas (no, no le pifié a un cero));
  • Deja un montón de puertas abiertas para que, por más que sea un XML, se siga dependiendo de herramientas propietarias de Microsoft para trabajar con los archivos;

Microsoft alega que OpenDocument es insuficiente y tiene falencias... pero bueno, en lugar de trabajar para mejorar ese estándar, siguió impulsando OpenXML. Pero resulta que entonces apareció otro problema: OpenDocument está considerado un estándar internacional, y ha sido recomendado por más de un organismo gubernamental (de nuevo, en Europa, donde el marketing es un poquito menos descarnado...). Y OpenXML no. Así que Microsoft decidió pedirle a ISO que considere OpenXML como un estándar...

... pero siguiendo lo que se denomina "fast-track", que básicamente significa, no miremos en detalle la especificación (recuerden: más de 6000 páginas), primero definamos que es un estándar, y DESPUES, veamos que hay que arreglar. Mágicamente, varios países y organismos miembros de ISO que NO tenían poder de voto en esta decisión, solicitaron a último momento el "upgrade" de su status dentro de ISO para poder votar (¿lobby?). Así y todo, ganó la cordura. Por ahora, OpenXML no es un estándar.

Más info sobre la votación, en Ars Technica

Alguien podría decir, y con razón, "y quien sos vos para opinar, ¿acaso leíste las especificaciones?" No, claro que no. Ni siquiera la de OpenDocument, que es mucho más chica. Pero lo que yo digo es: NO necesitamos DOS estándares para documentos. Necesitamos UNO, y que sea bueno y completo. Entonces, si OpenDocument no sirve del todo, MEJOREMOS eso. Pero no hagamos otro.

Y por sobretodas las cosas: No hagamos otro que es CERRADO bajo la mentira de que es abierto, solo porque es un XML, y una empresa muy poderosa dice que es abierto.