tag:blogger.com,1999:blog-56445922024-03-07T05:57:31.857+01:00javier santana - blogJavi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.comBlogger20125tag:blogger.com,1999:blog-5644592.post-38232424118685918582011-04-08T18:11:00.005+02:002011-05-04T08:48:00.845+02:00whitebrd.me - detalles técnicosEl pasado fin de semana (primero de abril del 2011 por si lees esto en un futuro lejano) organizamos <a href="http://alltogether.es/">alltogethernow</a>, un encuentro de un fin de semana para hacer una aplicación en 48 horas. La aplicación que hicimos nosotros (@<a href="http://twitter.com/flopezluis">flopezluis</a> y yo)fue <a href="http://whitebrd.me/">whitebrd.me</a>, una pizarra compartida en tiempo real. Voy a dar una pinceladas de los detalles técnicos y un pequeño post-morten después de 1 semana funcionando.<div><br /></div><div>Para empezar optamos por usar toda la tecnología del servidor asíncrona. Pensarás que lo hicimos porque es lo que mola, ahora lo asíncrono está en todos lados, si no tienes algo asíncrono no puedes montar algo como dios manda... si toda la gente que se le llena la boca con "asíncrono" hubiese leído la famosa (en mi época) "<a href="http://beej.us/guide/bgnet/output/html/multipage/index.html">beej's socket guide</a>"... :).</div><div><br /></div><div>El caso es que <a href="http://www.tornadoweb.org/">tornado</a>, un pequeño framework web creado por friendfeed (después comprada por facebook) y ahora matenido por facebook, hizo las veces de servidor web y redis como sistema de persistencia. La elección de redis fue por dos razónes. La primera por hypearnos a más no poder y la segunda es que permite escribir muy rápido, tiene funcionalidad de publisher/sibscriber y un sistema de VM que encaja muy bien (luego veremos cómo). Para rematar usamos nginx como frontend. Se puede ser más asíncrono? :)</div><div><br /></div><div>La razón para usar asíncrono realmente es muy sencilla: es una aplicación con MUY poca carga de CPU y mucha E/S, así que el paradigma encaja perfectamente.</div><div><br /></div><div>En la parte de cliente usamos websockets para enviar todos los comandos y canvas para dibujar. Es una tecnología novedosa, así que sabíamos que muchos navegadores no lo soportarían (FF4 lo tiene desactivado por defecto, primer #FAIL).</div><div><br /></div><div>Manos a la obra, nos pusimos y en 48 horas teníamos <a href="https://github.com/javisantana/whitebrd.me">el código</a> en cuestión. Más que comentar el código, prefiero centrarme en las cosas que han pasado en estos días y algunas conclusiones técnicas que he sacado.</div><div><br /></div><div>- El segundo día a la gente de github les dio por poner un enlace en su <a href="https://github.com/blog/829-github-reflog-v1-4-04">blog</a>. No sé cual será el tráfico de ese blog, pero en el nuestro generó 4.5Gb de tráfico en 12 horas. Iluso de mi, no había activado la memoria virtual en redis, de forma que redis no podía tirar a disco las pizarras ya no usadas (<a href="https://github.com/javisantana/whitebrd.me/blob/master/src/models.py#L20">se almacenan todos los comandos que genera una pizarra</a>), así que empezó a "swapear" como un demonio. Suerte que teníamos el <a href="https://github.com/javisantana/whitebrd.me/blob/master/fabfile.py">deploy automático</a>, así que la activé rápido y medio solucionado. Finalmente la clave que almacenaba esa pizarra terminó con más de 20mb de datos. He tenido que eliminarla porque el VPS de 256mb no da para mucho más :). La CPU de la máquina no pasó de un 8%.</div><div><br /></div><div>- tornado funciona excepcionalmente bien, además de ser un framework muy interesante para cosas "sencillas" (no tiene ORM por ejemplo) es realmente rápido. Además el <a href="https://github.com/facebook/tornado/blob/master/tornado/ioloop.py">nucleo</a> es fácil de entender y está bien documentado. Podríamos haber optado por twisted, gevent o algún otro sistema asíncrono en python.</div><div><br /></div><div>- tratamos de usar el mecanismo <a href="http://redis.io/topics/pubsub">pub/sub de redis</a>, pero la librería asíncrona cliente redis para python es completamente inestable, así que terminamos por implementar lo misma funcionalidad en una <a href="https://github.com/javisantana/whitebrd.me/blob/master/src/publisher.py">pequeña clase </a>. Moraleja: a veces la solución más simple es la mejor.</div><div><br /></div><div>- no conocía redis, pero es realmente un descubrimiento. Funciona muy bien: el setup es muy rápido, prácticamente configuración 0, la integración con los tipos de python buena y además rápida. El model de memoria virtual encaja muy bien ya que si es necesaria más memoria las claves que no se usan las vuelca a disco, de forma que todas las pizarras que ya no se usan no están malgastando los 256mb memoria.</div><div><br /></div><div>Ahora mismo hay más de 1000 pizarras creadas y la mayoría de ellas tienen dibujos de aparatos reproductores masculinos :)</div><div><br /></div><div><br /></div><div><br /></div><div><br /></div>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com2tag:blogger.com,1999:blog-5644592.post-37679968381534454872010-10-31T22:11:00.008+01:002010-10-31T23:34:41.279+01:00scrapper multiproceso en python<div>Nota inicial: Si no te gusta python puede que este post te haga cambiar de opinión :)</div><div><br /></div>Una de las <a href="http://docs.python.org/dev/whatsnew/2.6.html#pep-371-the-multiprocessing-package">mejoras de Python 2.6</a> (en estos momentos vamos por la 2.7, que será la <a href="http://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x">última de la rama 2.x</a>) es el módulo multiprocessing. En pocas palabras viene a ser un módulo para trabajar con procesos de la misma forma que se hace con threads, de hecho en un subconjunto de la funcionalidad puedes cambiar threads por procesos cambiando un solo import.<div><br /></div><div>Sin embargo el módulo multiprocessing añade cosas muy interesantes como la posibilidad de trabajar con pool de procesos. Veamos un ejemplo.</div><div><br /></div><div>Imaginemos que tenemos que bajar una serie de ficheros pdf para posteriormente extraer información de ellos. Una primera aproximación sería esta:</div><br /><br /><pre><br />import urllib<br />import urllib2<br /><br />reg_nos = [16738, 17288, 18162, 18776, 18868, 19116, 19223, 19505];<br />pdf_url = 'http://www.mapa.es/agricultura/pags/fitos/registro/sustancias/pdf/%s.pdf'<br /><br />def fetch_url(url, params={}): <br /> return urllib2.urlopen(url).read() <br /><br />def save_url_as_file(url, filename):<br /> open(filename,'wb').write(fetch_url(url))<br /> <br />def download_pdf(reg_no):<br /> f = '%d.pdf' % reg_no<br /> save_url_as_file(pdf_url % reg_no, f)<br /> print "\t- %s downloaded" % f<br /><br /># tests<br />def single(regs):<br /> for u in regs:<br /> download_pdf(u)<br /><br />single(reg_nos)<br /></pre><br />(puedes verlo mejor con sintáxis coloreada en <a href="http://gist.github.com/657222">github</a>)<br /><br />Para 4 míseros ficheros no merece la pena hacer más, pero imaginemos que queremos bajarnos miles y que además lo tenemos que hacer periódicamente, el tiempo en bajarse todos esos ficheros es alto. Lo primero que se nos ocurre es usar concurrencia: lanzando una serie de hilos/procesos que vayan bajando los ficheros aceleraría sensiblemente el proceso (de hecho así lo hacen los navegadores cuando se bajan los ficheros que referencia el HTML).<br /><br />En python esto traducido a código ocupa mucho menos que explicarlo:<br /><br /><pre><br />def download_multi(regs, nprocesses=4):<br /> pool = Pool(processes=nprocesses) <br /> pool.map_async(download_pdf, regs).get()<br /></pre><br /><br />Usando multiprocessing.Pool python se encarga de lanzar los procesos y preparar una cola para enviarle a la función que especificamos en el primer parámetro. <br /><br />Este es un uso de <a href="http://docs.python.org/dev/library/multiprocessing.html#module-multiprocessing">multiprocessing</a>, pero tiene otros muchos muy interesantes.<br /><br />Podéis ver todo el <a href="http://gist.github.com/657219">código en github</a> y ejecutar el pequeño benchmark:<br /><pre><br />q6:smll javi$ python fetch.py <br /> - 16738.pdf downloaded<br /> - 17288.pdf downloaded<br /> - 18162.pdf downloaded<br /> - 18776.pdf downloaded<br /> - 18868.pdf downloaded<br /> - 19116.pdf downloaded<br /> - 19223.pdf downloaded<br /> - 19505.pdf downloaded<br />2.30190205574<br /> - 18776.pdf downloaded<br /> - 17288.pdf downloaded<br /> - 18162.pdf downloaded<br /> - 16738.pdf downloaded<br /> - 19116.pdf downloaded<br /> - 18868.pdf downloaded<br /> - 19505.pdf downloaded<br /> - 19223.pdf downloaded<br />0.807252883911<br /></pre><br />Un incremento un poco menor de 4X, el número de procesos que lanzo en el pool. <br /><br />Últimamente uso este módulo para muchísimas tareas ya que el uso es prácticamente directo si la aplicación está bien modularizada y permite aprovechar la potencia de las máquinas actuales (en mi caso un dual core).<br /><br /><span style="font-weight:bold;">Bonus Track - threads</span><br /><br />Con threads también es posible hacerlo, pero lamentablemente el módulo threading no tiene la funcionalidad Pool, así que debemos emularla. <br /><br />Antes de pasar a la implementeación está bien decir que desde hace cosa de dos años hasta ahora se ha criticado mucho el modelo multithread de python debido a que existe una cosa llamada GIL (Global Interpreter Lock) que hace que solo pueda estar ejecutándose un hilo al mismo tiempo en el intérprete python. A pesar de ser hilos nativos hay un lock que evita que dos hilos se puedan ejecutar al mismo tiempo. Si quieres saber un poco más sobre el GIL hay una <a href="http://www.dabeaz.com/python/GIL.pdf">presentación</a> excelente de maestro Dave Beazley. <br /><br />Es para llevarse las manos a la cabeza, pero esto no quiere decir que el desarrollo con hilos en python esté "prohibido", símplemente hay que saber para qué se puede o no usar. En este caso el uso de threads, a pesar del Lock es muy interesante, ya que al ser tareas fundamentalmente de Entrada/Salida no hay problemas de bloqueo entre hilos (la explicación más en detalle en la presentación que he citado antes).<br /><br />Sin más, usando Queue (otro módulo python mágico), una cola FIFO sincronizada la tarea es más o menos simple:<br /><br /><pre><br />def threaded(regs, nthreads=4):<br /> # ripped from http://www.dabeaz.com/generators/Generators.pdf<br /> def consumer(q): <br /> while True:<br /> item = q.get() <br /> if not item: break <br /> download_pdf(item)<br /><br /> in_q = Queue.Queue() <br /> <br /> # start threads<br /> ths = [threading.Thread(target=consumer,args=(in_q,)) <br /> for th in xrange(nthreads)]<br /> for x in ths: x.start()<br /><br /> # put files to download<br /> for i in regs:<br /> in_q.put(i)<br /><br /> # put end guards<br /> for th in xrange(nthreads): in_q.put(None)<br /><br /> # wait to finish<br /> for x in ths: x.join()<br /></pre>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com2tag:blogger.com,1999:blog-5644592.post-11132592125014625922010-07-11T18:12:00.003+02:002010-07-11T18:31:20.620+02:00testing y deploy con pythonLa pasada semana hicimos una ronda de <a href="http://en.wikipedia.org/wiki/Lightning_Talk">lightning talks </a> con el objectivo de introducir a los compañeros de la empresa en tecnologías que no habían usado hasta ahora. Además sirvieron para hacer un poco de team building y romper un poco el hielo, cosa importante en un equipo _muy_ distribuído. Lo empecé a ver a la gente de <a href="http://vimeo.com/channels/aspgems">aspgems</a> y me pareció interesante.<br /><br />Por mi parte hice un par de charlas que puede que resulten interesantes:<br /><br />- <a href="http://javisantana.com/python/testing/">Introducción al testing con python y django</a> (<a href="http://javisantana.com/python/testing/presentation.pdf">pdf</a>). Repasa conceptos muy básicos del testing y trata de explicar como testear una aplicación en django. Es muy muy básico, pero puede servir de ayuda si no estás familiarizado.<br /><br />- <a href="http://javisantana.com/python/deploy/">Introducción a fabric</a> (<a href="http://javisantana.com/python/deploy/presentation.pdf">pdf</a>). Fabric es una herramienta para facilitar la automatización de los "deploys". Es similar a capistrano en ruby o ant (salvando las distancias) en java.<br /><br />Como curiosidad las transparencias están creadas con una aplicación llamada <a href="http://github.com/adamzap/landslide">landslide</a>, que transforma sintáxis markdown a una presentación HTML5 o PDF. Muy simple de usar y fácilmente "maqueable".<br /><br />Me gusta mucho dar charlas de este tipo -a pesar de mi nula capcidad comunicativa- y creo que dice mucho el que un equipo de desarrollo monte charlas de este tipo para estar al día. A ver si mis compañeros se animan a subir las suyas.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com3tag:blogger.com,1999:blog-5644592.post-78503490599975871272010-06-09T20:59:00.003+02:002010-06-09T21:05:48.508+02:00Testing con datetime en pythonEste es un pequeño "truco" para testear métodos o funciones que usen <a href="http://docs.python.org/library/datetime.html">datetime.now</a>. Se podrían usar trucos aprovechando que python es un lenguaje muy dinámico, pero siempre que se pueda hacer explícito y simple, para qué complicarnos?<br /><br /><pre><br />def method(param1, param2, now=None): <br /> now = now or datetime.now()<br /> # do something with now<br /> pass<br /></pre><br /><br />En el uso normal la llamaremos normalmente, pero en el test podremos pasarle un datetime concreto para testear.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com1tag:blogger.com,1999:blog-5644592.post-40348756868701021742010-03-21T18:02:00.003+01:002010-03-21T18:18:23.684+01:00Como hacer un demonio en pythonSiempre llega ese día en el que necesitas tener un <a href="http://es.wikipedia.org/wiki/Demonio_(inform%C3%A1tica)">demonio</a> funcionando en una máquina. Y cuando llegas ves que necesitas hacer un par de forks, cosas con stdin y stdout... un peñazo, por suerte en python existe una librería llamada <a href="http://pypi.python.org/pypi/python-daemon/">python-daemon</a>, aunque no está demasiado bien documentada (hay que bucear un poco en los fuentes), es muy útil para no tener que liarte demasiado para hacer el demonio. Aquí demo un <a href="http://gist.github.com/339430">ejemplo de uso de python-daemon</a> con su log y sus redirecciones de los std*.<br /><br />Para ejecutar el demonio se usa el interfaz típico de start|stop|restart, por ejemplo:<br /><blockquote> $ python daemon.py start</blockquote><br /><br /><br /><script src="http://gist.github.com/339430.js"></script>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com1tag:blogger.com,1999:blog-5644592.post-40927630407876088592010-03-03T16:33:00.003+01:002010-03-03T22:52:52.066+01:00Consejos para trabajar con pythonCada lenguaje tiene sus herramientas, su forma de trabajar, sus reglas de estilo, etc... aparte cada uno tenemos nuestros pequeños trucos y reglas para trabajar lo más ordenado posible. Por ello voy a dar una serie de consejos que he ido aprendiendo a medida que he ido programando en python que espero puedan ser de utilidad a otras personas que así lo hagan.<br /><br />- Sigue las reglas de python. Para conocerlas basta con que ejecutes python -c "import this" en la consola. No son específicas python, creo que sirven para cualquier lenguaje, muy recomendable "lectura".<br /><br />- Trata de seguir las recomendaciones de la guía de estilo del <a href="http://www.python.org/dev/peps/pep-0008/">PEP-008</a> (Style Guide for Python Code). Los PEP (Python Enhancement Proposals) son documentos que se redactan y siguen cuando se implementa una nueva característica. <br /><br />- Aprovecha la potencia del lenguaje. Si sabes ruby o similar sabrás de lo que hablo, los que vienen de java, C, C++ están acostumbrados a lenguajes más bien estáticos, se usa poco la metaprogramación. Python tiene cosas muy interesantes, por ejemplo las <a href="http://docs.python.org/tutorial/datastructures.html#list-comprehensions">list comprehesions</a> que agilizan mucho el desarrollo. Siempre con mucho cuidado de no pasarse (la gente de ruby me entenderá). Por ejemplo, la gente que viene de java suele escribir interfaces y luego heredar para a implementación. En python directamente usa <a href="http://es.wikipedia.org/wiki/Duck_typing">duck typing</a>. <br /><br />- Cuidado con los imports. Salvo que sepas lo que haces trata de no hacer "from module import *". Primero porque si lo haces posiblemente es que no sepas ni lo que quieres usar, segundo porque puede ser una fuente de bugs importante. Es mejor hacer "from module import MyClass, MyClass2". A mi me gusta primero poner los imports de la librería estandard y después lo propios de la aplicación, pero es algo personal.<br /><br />- Unicode: siempre especifica la codificación del fichero, en el <a href="http://www.python.org/dev/peps/pep-0263/">pep-0263</a> tienes toda la información, pero el resumen es, pon la siguiente cabecera:<br /><blockquote> <br />#!/usr/bin/python<br /> # -*- coding: <encoding name> -*-<br /></blockquote><br />Si no lo haces tarde o temprano tendrás alguna excepción al ejecutar tu código por haber puesto caracteres fuera del ascii. Además, trata de entender como qué es unicode, como <a href="http://www.amk.ca/python/howto/unicode">se hace para usarlo en python</a>.<br /><br />- Excepciones. No captures una excepción y no hagas nada. La gente que programa en java lo puede hacer (y de hecho lo hace), pero si eres un hombre de bien haz algo. Captura la excepción que corresponde, esto es, si esperas un socket.error no pongas un except que recoja a troche y moche, solo captura esa excepción.<br /><br />- Las doctest son una verdadera virguería para documentar a la vez que testear. No en todos los sitios se pueden usar, pero por ejemplo si al comienzo de un módulo pones una explicación usando doctest mejorará bastante, por ejemplo:<br /><br /><blockquote> <br />"""<br />Este módulo sirve para sumar 3 números usando la función add3.<br />Un ejemplo de uso es:<br />>>> add3(1,2,3)<br />6<br />""""<br />def add3(....<br /></blockquote><br /><br />Luego desde la consola puedes hacer:<br /><blockquote> <br />python -m doctest modulo.py<br /></blockquote><br /><br />y ver si algo casca. Más información de <a href="http://docs.python.org/library/doctest.html">doctest y ejemplos</a>. Además luego hay el módulo unittest, hay frameworks para stubs y mocks, helpers para facilitar los test como <a href="http://somethingaboutorange.com/mrl/projects/nose/0.11.1/">nose</a> y <a href="http://codespeak.net/py/dist/test/">pytest</a>, todo lo necesario para estar a la última moda del TDD.<br /><br />- usa la consola. Unas de las cosas más útiles es que tienes la consola siempre a mano para probar cosillas. La mayoría de las veces no me acuerdo como hacer algo, en la consola lo pruebas instantaneamente. Recomiendo usar <a href="http://ipython.scipy.org/moin/">ipython</a>, que viene ser igual que la consola original python, pero con facilidades para acceder a la documentación, autocompletado, etc.<br /><br />- Usar <a href="http://pypi.python.org/pypi/pip">pip</a> o easy_install. Es similar a gem en ruby, lo que hace es instalar módulos de terceros desde internet con un solo comando. Esto es fundamental y agiliza el desarrollo muchísimo. Para buscar el software usa <a href="http://pypi.python.org/pypi">Python Package Index</a> donde tú mismo puedes subir tus paquetes. De hecho el paquete de la librería standard para empaquetar módulos lo sube automáticamente con un comando. Prefiero pip a easy_install porque pip está construído con más lógica (tiene uninstall) y más características que cuento dos puntos más abajo.<br /><br />- Haz pequeños módulos y empaqueta. Empaquetar es súmamente sencillo, con hacer un fichero setup.py con pocos parámetros tienes el empaquetado hecho. Aparte de los benficios que tiene separar las aplicaciones en modulos, tiene otros muy interesantes. El módulo que se usa para estas cosas es distutils, donda hay un <a href="http://docs.python.org/distutils/introduction.html#a-simple-example">ejemplo de setup.py</a> muy sencillo.<br /><br />- Usa pip. Sí, es repetido, pero pongamos que tienes el caso que has separado tu aplicación en varios módulos que tienes en un repositorio. Desarrollas una aplicación que los necesita, pip te va a ayudar a resolver este problema ya que puede instalar módulos directamente del repositorio (siemre que tengan su setup.py), por ejemplo:<br /><br /><blockquote> <br />pip install svn+https://mirepo/project/module#egg=module<br /></blockquote><br /><br />Todos los paquetes necesarios se pueden poner en un fichero requirements.txt que todas las aplicaciones tengan.<br /><br />- virtualenv. Herramienta fundamental, permite aislar entornos de ejecución. Puedes crear tantos entornos como quieras, cada uno con una versión de python diferente, cada uno con sus librerías. Esta herramienta es, repito, fundamental. Yo lo uso junto a <a href="http://pypi.python.org/pypi/virtualenvwrapper">virtualenvwrapper</a> que simplifica la creación y gestión de entornos. Además junto con pip hacen el combo perfecto. Por ejemplo, para tener un entorno de trabajo funcionando basta con hacer:<br /><br /><blockquote> <br />pip install -E env -r requirements.txt<br /></blockquote><br />pip creará un entorno nuevo, e instalará todo los especificado en el fichero requirements.txt, bajándose lo necesario. Luego basta con activar el entorno y a trabajar:<br /><br /><blockquote> <br /> . ./env/bin/activate<br /></blockquote><br /><br />- Aprender a usar el debugger, pdb. Sé que ya no mola hacer debug en la línea de comando, que eclipse te lo da todo mascadito, pero si estás en un servidor remoto es útil cuando solo tienes ssh y vim :).<br /><br />- Usa el log. Python tiene un módulo llamado logging, úsalo, luego se puede configurar para redireccionar a un fichero, al syslog, a stdout. Tiene las típicas error, info, debug... pero lo importante es que lo uses. <br /><br />En general, cuando no sé la mejor forma de hacer algo, suelo ir al código de la librería standard y mirar paquetes que suelo usar.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com1tag:blogger.com,1999:blog-5644592.post-83631192601229560802010-02-23T13:05:00.002+01:002010-02-23T13:16:31.287+01:00Pycon 2010 ClosingEchando un ojo a la <a href="http://theonda.org/pages/pycon2010">closing de la pycon</a> de este año leo la siguiente frase:<br /><br /><blockquote>I've always found a way to use Python as a strategic weapon, a tremendous source of leverage, to make that small group of people much more productive than the big companies that can— through the force of sheer mass— crush you like a bug.</blockquote>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com0tag:blogger.com,1999:blog-5644592.post-26869606115175908132009-10-31T13:20:00.002+01:002009-10-31T13:24:32.160+01:00Python y miniframeworks webHe empezado a colaborar con web.ontuts, una blog sobre <a href="http://web.ontuts.com/">tutoriales de programación web</a>, con un pequeño artículo sobre juno, un pequeño framework web hecho en python muy útil para pequeños servicios.<br /><br />Aquí está:<br /><a href="http://web.ontuts.com/tutoriales/mini-aplicaciones-web-con-python-y-juno-parte-i/">mini-aplicaciones web con python y juno</a>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com2tag:blogger.com,1999:blog-5644592.post-293042758619488582009-10-23T19:41:00.005+02:002009-10-23T20:13:11.319+02:00integración continua en 100 líneas de códigoActualmente estoy trabajando con un embebido con linux, la potencia de la placa es bastante limitada y a la hora de compilar una aplicación mediana el sistema demasiado. Como tengo varias placas decidí montar un servidor de integración continua en uno de ellos usando compilación distribuída con <a href="http://distcc.samba.org/">distcc</a>. La forma de trabajar es la siguiente:<br /><br />- programo haciendo mis commits en local (usando git)<br />- cuando necesito probar mi código en el embebido hago "git push ci" de forma que ci es un remote parecido a ssh://user@ciserver/home/ci/project. <br />- Este repositorio tiene un <a href="http://www.kernel.org/pub/software/scm/git/docs/githooks.html">post-recieve-hook</a> que lanza una petición al servidor de integración continua, este compila (distribuido, con distcc) y si la compilación va bien lanza un "build_pass" que usando <a href="http://samba.anu.edu.au/rsync/">rsync </a>hace deploy en la máquina de pruebas.<br /><br />Parece muy complicado, pero realmente es poca la configuración que se necesita, casi todo va sobre ssh.<br /><br />Sentía la necesidad imperiosa de tener un servidor de integración continua pequeño y manejable, así que decidí hacer uno :). <br /><br />Con <a href="http://github.com/breily/juno">juno</a>, un miniframework web que permite en dos patadas tener una pequeña aplicación web.<br /><br />Si quereis probarlo o ver el código, el código está en github: <a href="http://github.com/javisantana/cipy">cipy, servidor de integración continua</a>.<br /><br />Funciona bien, realmente no hace demasiado, pero basta. Solo soporte un proyecto, pero no hay problema para lanzar varias instancias, cada una en un puerto diferente, apuntando ngnix ( o tu servidor web favorito) con un "proxy pass" a cada una con un "location" diferente.<br /><br />Si realmente quieres un servidor de integración continua potente puedes usar <a href="https://hudson.dev.java.net/">hudson</a>.<br /><br />Un shot:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHNQQpAfgWHB6YxgDhADBHy7x-rbDrPf5pD8bAs81UlRaR0CpVLerCVSH5lguQOMhVluu-_cD3mImdsoNQnoMgxOX43ORlwc0axctmXT-ydQWZVCH9kFdYbyT01p_0xy-tPa0fIw/s1600-h/cipy_2.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 211px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHNQQpAfgWHB6YxgDhADBHy7x-rbDrPf5pD8bAs81UlRaR0CpVLerCVSH5lguQOMhVluu-_cD3mImdsoNQnoMgxOX43ORlwc0axctmXT-ydQWZVCH9kFdYbyT01p_0xy-tPa0fIw/s320/cipy_2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5395858444104647426" /></a>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com0tag:blogger.com,1999:blog-5644592.post-69982402374461280472009-04-27T22:03:00.003+02:002009-04-27T22:15:44.241+02:00Filtro paso bajo con pythonEs muy común tener un señal con mucho ruido, si es de un GPS más aún y normalmente interesa que los movimientos sean suaves. Bien sabido es que con un filtro paso bajo podemos atenuar el ruido y hacer que todo sea suave y maravilloso.<br /><br />Si además no tenemos que filtrar al vuelo, esto es, tenemos ya toda la señal bien guardadita en un array, es posible usar el truco de teleco viejo, utilizar la fft. ¿Cómo? pues símplemente haciendo la transformada discreta de la señal, quitando los armónicos más altos y haciendo la transformada inversa.<br /><br />Aquí el código, todo gracias a numpy :)<br /><br />from numpy import fft<br /><br />def low_pass_filter(x, samples = 20):<br /> """ fft based brute force low pass filter """<br /> a = fft.rfft(x)<br /> tot = len(a)<br /> for x in xrange(tot-samples):<br /> a[samples + x] = 0.0<br /> return fft.irfft(a)<br /><br /><br />El código seguro que es mejorable, numpy tiene métodos para trabajar con arrays de forma eficiente, etc, pero funciona a las mil maravillas y permite un control bastante lógico, cuantos más samples de la fft no sean 0, mayor será la variación de la señal. Para que luego digan que lo que se aprende en la carrera no sirve de nada...Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com2tag:blogger.com,1999:blog-5644592.post-30580306128691482322009-03-13T14:03:00.003+01:002009-03-13T14:56:27.345+01:00Planificando un deploy (django + nginx)Si algo he aprendido a lo largo de mi corta vida como perfil mixto entre desarrollador web y adminitrador de sistemas es que los <span style="font-weight:bold;">deploys sí importan</span>. Ahora mismo tengo una aplicación web en django y mis <span style="font-weight:bold;">requisitos</span> para el deploy son los siguientes (lo cierto es que servirían para cualquier aplicación web):<br /><br />- Hacer el setup del servidor en un solo paso<br />- <span style="font-weight:bold;">Poder tener la aplicación en el servidor funcionando con un solo comando</span><br />- Poder volver a una versión anterior en cualquier momento<br /><br />Simples de describir, pero complicados de llevar a cabo.<br /><br />Hay 3 cosas que tengo que tener en cuenta en la <span style="font-weight:bold;">configuración</span>:<br />- el servidor web<br />- la aplicación<br />- la base de datos<br /><br />Por mi parte he elegido nginx como servidor web ya que soporta fastcgi y parece ligero, para la aplicación uso django y como base de datos mysql. La elección no se basa nada más que en mi experiencia, no quiero entrar en el juego de que es mejor o peor.<br /><br />Para el deploy estoy usando <a href="http://www.nongnu.org/fab/">fabric</a>, un sistema que permite en 3 puntos:<br />- ejecutar comandos en local<br />- ejecutar comandos en un server remoto<br />- subir y bajar ficheros<br /><br />Y todo con sintaxis python :), con lo cual puede además usar todo el api de python.<br /><br />El <span style="font-weight:bold;">layout de carpetas</span> es el siguiente:<br /><br /> - /srv/agroguia/<br /> - versions<br /> - 0<br /> - timestamp<br /> - ....<br /> - last (enlace simbólico a la última versión subida de esta versión)<br /> - 1<br /> - ...<br /> - current (enlace simbólico a la carpeta dentro de versions/X/timestamp)<br /><br /><br />El <span style="font-weight:bold;">servidor web</span> está dividio en dos rutas:<br /> - la parte estática que apunta a current/assets. De momento el peso de los assets es muy bajo (<1M), puedo permitirme el tenerlos replicados.<br /> - la parte dinámica que usa fastcgi contra un socket unix que se crea al levantar django.<br /><br />Y por qué dividir la aplicación en versiones y dentro de cada una en timestamp (en realidad timestamp + hash de la revisión del sistema de control de versiones). Cada versión tiene un esquema de base de datos y una base de datos diferente dentro de mysql, de forma que todas las versiones de la aplicación dentro de esa carpeta pueden usar la misma base de datos sin problemas de integridades ni nada por el estilo. Similar a este sistema de versiones y timestamps lo usa el sistema de deploy de google app engine.<br /><br />Del mismo modo, cada vez que cambie el esquema de la base de datos, se creará una carpeta nueva, se llamará al comando de creación de base de datos de django (manage.py syncdb) y luego llamaré a la migración (manual, django aún no soporta migraciones al estilo rails, una pena) que usará los datos de la versión anterior.<br /><br />Si en cualquier momento quiero volver a una versión anterior puedo símplemente cambiar el enlace simbólico de current y levantar de nuevo el servidor. Incluso si quiero tener una versión en producción y una para desarrollar basta con que levante un servidor de desarrollo en otro puerto diferente al 80 (google en este caso lo hace con subdominios, pero yo no soy tan guay)<br /><br />Otro detalle importante es la posibilidad de hacer un <span style="font-weight:bold;">setup del sistema desde 0</span>. Me baso en un servidor ubuntu, así que tengo unos cuantos targets que instalan dependencias (mercurial, nginx...), módulos python con <a href="http://pypi.python.org/pypi/pip">pip</a> (el reemplazo de easy_install), carpetas, usuarios y permisos varios.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com0tag:blogger.com,1999:blog-5644592.post-12479659478528567942009-01-07T22:36:00.002+01:002009-01-07T22:48:15.588+01:00twitter y sus posibilidadesNo deja de ser una pérdida de tiempo como otra cualquiera, o una herramienta de comunicación cojonuda, según se mire, pero para algunas cosas sí que puede resultar útil. Por ejemplo, quien no ha estado esperando para ir a recoger a alguien a la estación de tren o autobús y haciendo la típica llamada "¿por dónde vas?".<br /><br />Imaginemos que yo me pudiese suscribir a un usuario de twitter que me diese información del recorrido del tren o del autobús, por ejemplo. Bien es cierto que hay otros medios (rss, google earth, google maps, etc) para indicar esto, pero es un modo que para mi sería cómodo.<br /><br />Los que trabajamos en el parque tecnológico de boecillo tenemos un blog donde ponen los <a href="http://comedoresdelptb.blogspot.com/">menús diarios</a> en los diferentes comedores. Por qué no usar twitter para anunciarlo? En un rato me he puesto a hacer un rss2twit y ya de paso probar <a href="http://www.feedparser.org/">feedparser</a>, <a href="http://code.google.com/p/python-twitter/">twitter python</a> y <a href="http://www.crummy.com/software/BeautifulSoup/">BeautifulSoup</a>. El resultado un <a href="http://code.google.com/p/rss2twitter/source/browse/#svn/trunk">pequeño script</a> que postea los menús en <a href="http://twitter.com/comedoresdelptb">@comedoresdelptb</a>Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com5tag:blogger.com,1999:blog-5644592.post-92225138357716109252008-10-29T20:53:00.005+01:002008-10-29T20:55:51.561+01:00Ya están aquíPor fin han llegado:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbI1jCY8fVLuhhf2VJoWGCJZpEc0Jqv75sZbMncB6xntuWs9ubY3EIeGj2R-xMP4lj5I_5nqVAlY3GRKa2Imjz2NqpKGCf5_GLHt-KlVEhSvzoQjL4cKoxSEI7CGhp3e5J4Un8FA/s1600-h/30102008392.jpg"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 307px; height: 230px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbI1jCY8fVLuhhf2VJoWGCJZpEc0Jqv75sZbMncB6xntuWs9ubY3EIeGj2R-xMP4lj5I_5nqVAlY3GRKa2Imjz2NqpKGCf5_GLHt-KlVEhSvzoQjL4cKoxSEI7CGhp3e5J4Un8FA/s200/30102008392.jpg" alt="" id="BLOGGER_PHOTO_ID_5262666534535240322" border="0" /></a><br /><br />Mis pegatinas de python!Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com1tag:blogger.com,1999:blog-5644592.post-10027034021822302262008-08-13T23:09:00.003+02:002008-08-13T23:32:25.302+02:00Caché Opengl con PythonLos <a href="http://www.python.org/dev/peps/pep-0318/">decorators en python</a> son tremendamente útiles, cada día veo cosas más interesantes creadas con decoratos. Últimamente sobretodo relacionadas con Django (me imagino que por su pronta 1.0), para temas de cachés.<br /><br />Como python es bastante más lento que C++, cuando renderizo geometría estático con python la máquina tiende a morirse donde con c++ iría suavemente. Lo habitual en OpenGL es usar listas precompiladas para optimizar geometría estática, una especie de caché que deja en gpu los datos a renderizar.<br /><br />tenemos el siguiente método:<br /><br /><blockquote><br />def draw_complex_geometry():<br /> glBegin(GL_QUADS);<br /> for x in vertex:<br /> ....<br /> glEnd();<br /></blockquote><br /><br />Nada impide hacer el siguiente decorator:<br /><br /><b>def</b> list_compiler(fn):<br /> fn.__gl_compiled = -1;<br /> <b>def</b> render():<br /> <b>if</b>(fn.__gl_compiled < 0):<br /> fn.__gl_compiled = glGenLists(1);<br /> glNewList(fn.__gl_compiled,GL_COMPILE);<br /> fn();<br /> glEndList();<br /> <b>else</b>:<br /> glCallList(fn.__gl_compiled);<br /> <b>return</b> render;<br /><br />de esta forma decoramos el método:<br />@list_compiler<br />def draw_complex_geometry()...<br /><br />De forma que la primera vez que se llame compilará ls lista opengl y las siguientes veces símplemente llamará a la geometría "cacheada"Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com0tag:blogger.com,1999:blog-5644592.post-90290339939755785822008-04-24T23:54:00.003+02:002008-04-25T00:01:30.300+02:00Python, generators y pipesPara rematar el artículo del otro día sobre <a href="http://blep.blogspot.com/2008/04/generators-en-python.html">generators en python</a> conviene leerse este otro sobre <a href="http://wordaligned.org/articles/takewhile-drops-one">Pipelined Python</a> que no deja de ser <a href="http://en.wikipedia.org/wiki/Syntactic_sugar">syntactic sugar</a> (como diría <a href="http://www.lacoctelera.com/antoniogarrote">alguno</a>), pero queda la mar de c00l.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com0tag:blogger.com,1999:blog-5644592.post-11537096811866439132008-04-17T22:15:00.003+02:002008-04-17T22:18:47.602+02:00generators en pythonA través de planet python veo una presentación sobre el uso de <a href="http://www.dabeaz.com/generators/">generators en python</a>. Explica desde lo más básico hasta frikadas insospechadas, merece la pena <a href="http://www.dabeaz.com/generators/Generators.pdf">leerlo</a>, muy ameno e instructivo. No sé, la verdad, si tendrá mucha utilidad real para casos más complejos, pero ahí queda.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com3tag:blogger.com,1999:blog-5644592.post-27926873280906134352008-03-23T21:43:00.005+01:002008-03-29T23:02:39.852+01:00clase transaccional en pythonDespués de unas "largas" vacaciones sin tocar el pc (apenas recuerdo donde están las teclas :) apetece leerse algún buen artículo, como por ejemplo uno de <a href="http://www.harald-hoyer.de/linux/pythontransactionclass">clases transaccionales en python</a>.<br /><br />Interesante artículo por varios motivos:<br /> <br />- La propia clase, personalmente creo que puede ser bastante útil, luego pongo un ejemplo<br />- El uso de introspection (o reflexion o como quiera que se llame) en python. Simple y efectivo<br />- La explicación, paso a paso, y el código final con sus test unitarios. <br /><br />Este es el típico ejemplo de pequeña clase que se complica y que termina siendo un verdadero infierno si no se tienen claros los contratos. Personalmente he tenido muy malas experiencias con clases en teoría simples, pero que dado su uso intensivo terminan por matar una aplicación. Por ejemplo, una clase tan simple como un vector, que en resumen no dejan de ser 4 métodos, es usada en todo el código, seguramente por varias personas que no tendrán ni idea de como está implementada (con razón), de la cual se pueden sacar unas cuantas "condiciones de contorno" que pueden hacer que la aplicación fracase estrepitosamente ya que cada persona puede decir: "es que yo pensé que funcionaba así"<br /><br />En cuanto a la clase transaccional, se me ocurre un uso muy práctico. Estamos acostumbrados a ver diálogos wizards y configururaciones en todas las aplicaciones. El usuario cambia valores, toquetea y al final pulsa sobre 'Ok' o sobre 'Cancel'. El planteamiento de la lógica del diálogo podría ser el siguiente:<br /><br />- al comienzo del diálogo se hace una copia de los datos.<br />- se modifica la copia en función de los eventos de usuario<br />- si el usuario acepta, se vuelcan los cambios que están en la copia en los datos originales.<br /><br />Queda mucho más elegante el siguiente funcionamiento:<br />- se modifican los datos (que implementan el modelo transacional) en función de los eventos de usuario. <br />- si el usuario cancela se hace rollback.<br /><br />Pero es que además, con este modelo tenemos solucionado el típico undo que tantos quebraderos de cabeza da de forma "transparente" (de hecho implementa el típico patron memento). Si unes esto a una serialización como dios manda ya tienes solucionado medio modelo de datos de la aplicación :). <br /><br />Eso sí, la clase tiene varios problemas, por lo menos dos que yo vea:<br />- si hay atributos muy pesados en 4 commits te has zumbado unos megas de ram y estos lenguajes dinámicos no son precisamente ahorradores en este aspecto<br />- a poco mal que hagas el modelo de datos habrá variables que no te interese, perdón, que no deban guardar el estado. Pasa exactamente lo mismo que con la serialización.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com0tag:blogger.com,1999:blog-5644592.post-60599080386545509362008-02-28T20:34:00.003+01:002008-02-28T20:56:15.637+01:00java... oh noJava no me gusta y no me gusta por muchas cosas que ya he comentado, odio ese quiero pero no puedo, ni es totalmente dinámico ni totalmente dinámico, ni es multiplataforma ni deja de serlo... y es que java ahora mismo está a medio camino entre C++ y otros lenguajes de alto nivel como python, ruby o C#.<br /><br />El caso es que llevo unos días trabajando con python para diferentes tareas de administración y automatización y te me doy cuenta que soy mucho más productivo y puedo dedicar el tiempo a otras cosas que no sean poner try catch, casts e interminables líneas para crear una simple lista.<br /><br />A nadie que tenga cierta experiencia en programación se le escapa que las listas, maps, sets y demás son estructuras de datos básicas, que se usan para casi absolutamente todo y por tanto que el lenguaje los tenga "siempre a mano" y mantenga cierta simplicidad en su uso es vital. Por ejemplo, en java para filtrar una lista tienes lo siguiente:<br /><br /><blockquote><br />void filter(List<type> src) {<br /> List<type> li = new ArrayList<type>();<br /> for (Type t: src) {<br /> if( t != null)<br /> li.add(t);<br /> }<br />}<br /></blockquote><br /><br />Menudo coñazo, es que dan ganas de morir según lo tecleas... la misma cosa en python:<br /><blockquote><br />li = [t for t in src if t != None];<br /></blockquote><br /><br />Una cosa tan simple se convierte en algo tedioso, cuando tienes que hacer bastantes operaciones con listas, maps, etc, ya es el súmun. Y no se trata de apelotonar todo en una línea, al final eso es una bomba de relojería para un proyecto, pero tampoco estamos hablando de un tema complejo, es una simple lista que usamos para absolutamente todo. Ya no digo nada cuando veo frameworks como ruby on rails, con el que recientemente he tenido algo más de contacto.Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com3tag:blogger.com,1999:blog-5644592.post-90633483537073749152006-11-27T21:15:00.000+01:002006-11-27T21:29:36.202+01:00Compartiendo datos entre python y C++Es muy babitual usar python como herramienta para labores anexas a otras, por ejemplo para extraer datos de un fichero de texto, hacer tareas previas la compilación y multitud de cosas. De esta forma podemos aprovechar la capacidad de tratar texto u otros datos de python (y su amplia librería) para dejarselo fácil a la aplicación en C++. Y qué significa dejarselo fácil? pues en pocas palabras en dejarle un fichero que podamos leer directamente a estructuras y que no tengamos que parsear ficheros de texto.<br /><br />Para ello python provee un módulo cojonudo, struct. Con él en dos líneas podemos guardar datos que posteriormente podemos leer en python, por ejemplo.<br /><br />- python<br />open("fichero.bin","wb").write(struct.pack('3f2f',x,y,z,u,v));<br /><br />- C++<br />struct vertex <br />{ <br /> float x,y,z; <br /> float u,v;<br />};<br /><br />vertex v;<br />FILE* f = fopen("fichero.bin","rb");<br />if(f)<br /> fread(&v,1,sizeof(vertex),f);<br /><br />Con este módulo podemos hacer, por ejemplo, un exportador de geometría en python para Blender en cosa de segundos:<br /><br />scn= Blender.Scene.GetCurrent()<br />object= scn.getActiveObject()<br />mesh = BPyMesh.getMeshFromObject(object, None, True, False, scn)<br />file = open(filename, 'wb')<br />file.write(pack('i',len(mesh.faces));<br />for f in mesh.faces:<br /> for v in f.v:<br /> file.write(pack('3f',v.co[0],v.co[1],v.co[2]);Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com1tag:blogger.com,1999:blog-5644592.post-80934980957669011862006-11-21T22:02:00.000+01:002006-11-21T23:17:25.600+01:00Juegos cortos, ideas buenasVeo en el blog de librador, un empleado de digital <STRIKE>leyends </STRIKE> legends (jaja), <a href="http://www.librador.com/index.php?/archives/41-The-72-Hour-Game-Development-Competition.html">el juego que ha presentado</a> a una compo de creación de <a href="http://www.72hourgdc.com/">juegos en 72 horas.</a>. EL juego es un puzzle en el que tienes que mover unas bolas metálicas usando unos "electroimanes" para llevarlas a un agujero. Hay solo 4 ó 5 niveles creados, pero sirven para ver que el juego mola y plantea un reto interesante, a pesar de estar programado en 72 horas. <br /><br />Además el juego está creado en python + pygame y tiene todo el código a la vista :).Javi Santanahttp://www.blogger.com/profile/07638486113933454853noreply@blogger.com2