Hernán Grecco: A Pint a day ...

   Publicado: 2013-05-13 03:29:00

A few months ago I wrote about Pint, a Python units library that we developed for Lantz. With Pint  you can define physical quantities: the product of a numerical value and a unit of measurement. Pint can convert between units and perform mathematical operation between quantities.

For example:
>>> from pint import UnitRegistry
>>> ureg = UnitRegistry()
>>> my_beer = ureg.pint
>>> print(my_beer.to('liter'))
0.473176473 liter
>>> print(my_beer.dimensionality)
[length] ** 3
>>> ureg.pint < ureg.imperial_pint
True
>>> ureg.pint < ureg.liter
True

As described in the Design principles section of the documentation, units definitions are loaded from a text file which is simple and easy to edit. Adding and changing units and their definitions does not involve changing the code. Prefixed and pluralized forms of units are recognized without explicitly defining them. In other words: as the prefix kilo and the unit meter are defined, Pint understands kilometers. This results in a much shorter and maintainable unit definition list as compared to other packages. Pint also supports advanced string formatting using PEP 3101 syntax. Extended conversion flags are given to provide latex and pretty formatting.

After the previous post, I got valuable comments, suggestions, bug reports and patches. Today we are releasing Pint 0.2 with a lot of nice new features.

Extended NumPy Support

Pint does not require NumPy but provides support for NumPy arrays and functions since version 0.12. You can create a Quantity based on a ndarray:
>>> x1 = numpy.asarray([2., 3.]) * ureg.meter
>>> # or simply by multipliying an iterable by the units.
>>> x1 = [2., 3.] * ureg.meter
Pint 0.2 extends the support to almost all ndarray methods and ufuncs. Each function/method has been tagged according to expected units and how to handle arguments and Pint attempts to convert them on the fly. For example, arctan2 expects two arguments with the same dimensionality and converts the second argument to the units of the first:
>>> x2 = [2., 3.] * ureg.meter
>>> x2 = [300., 200.] * ureg.centimeter
>>> numpy.arctan2(x1, x2)
<Quantity([ 0.5880026 0.98279372], 'radian')>
The cosine function expects a quantity that can be converted to radians:
>>> numpy.cos(x1)
Traceback (most recent call last):
...
pint.unit.DimensionalityError: Cannot convert from 'meter' ([length]) to 'radian' (dimensionless)

Temperature conversion

Support for non-multiplicative (e.g. temperature) units was one of the most requested features. Pint is now able to to convert between different temperature scales:
>>> home = 25.4 * ureg.degC
>>> print(home.to('degF'))
77.7200004 degF

and also supports a multiplicative version for differences:
>>> difference = 5 * ureg.delta_degC
>>> print(difference.to('delta_degF'))
2.7777777777777777 delta_degF

For practicality, the parser infers from the units if you mean a difference or not:
>>> print(ureg.parse_units('degC/meter'))
delta_degC / meter

(of course, you can disable this behaviour if you want)

Pi Theorem

Buckingham π theorem states that an equation involving n number of physical variables which are expressible in terms of k independent fundamental physical quantities can be expressed in terms of p = n - k dimensionless parameters. Pint can find these dimensionless parameters:
>>> result = ureg.pi_theorem({'V': 'meter/second', 'T': 'second', 'L': 'meter'})
>>> print(result)
[{'V': 1.0, 'T': 1.0, 'L': -1.0}]
The returned value is a list containing the dimensionless parameters (in this case only one) each specified using a dictionary. The keys corresponds to the names of the physical variables and the values to the exponents. You can print it nicely using Pint's formatter function:
>>> from pint import formatter
>>> print(formatter(result[0].items()))
T * V / L
or using the dimensions
>>> result = ureg.pi_theorem({'V': '[speed]', 'T': '[time]', 'L': '[length]'})
>>> print(formatter(result[0].items()))
T * V / L 

Measurement

A new class provides provides support for Measurements: a value with uncertainty.
>>> length = (2.3 * ureg.meter).plus_minus(.23)
>>> print(length)
(2.3 +/- 0.23) meter
>>> print('{:.02f!p}'.format(length))
(2.30 ± 0.23) meter
>>> print('The relative error is: {}'.format(length.rel))
The relative error is: 0.1
The measurement class also supports some basic uncertainty propagation:
>>> width = (4.6 * ureg.meter).plus_minus(.23)
>>> print('{:.02f!p}'.format(width / length))
(2.00 ± 0.22)


Interested? Install it and give it a try!

Thanks to the people that contributed bug reports, suggestions and patches. In particular to: Richard Barnes, Alexander Böhn, Dave Brooks, Giel van Schijndel, Brend Wanders

Submit your bug reports, comments and suggestions in the Issue Tracker. There are already some ideas for version 0.3. Check them out, comment and add yours.

Read the docs: https://pint.readthedocs.org/
or fork the code: https://github.com/hgrecco/pint

Damián Avila: SciPyCon Argentina 2013

   Publicado: 2013-05-04 20:18:00

This is the first SciPy Conference in Argentina (as far as I know, it is also the first one in Latin American).

It will be held from 16 to 18th of May in Puerto Madryn, at the Patagonia Argentina (more details at SciPyCon homepage).

I am very proud to have this kind of conference in my country, and I would like to thanks to the organizers (and the sponsors) for all their hard work to make it happens.

The conference will have exciting talks, tutorials and poster presentations. You can check the schedule here

I will be presenting a tutorial called, Python Científico: Episodio I (Scientific Python: Episode I). You can check the details here.

And I will be also presenting a talk called, IPython notebook: el "paper ejecutable" (IPython notebook: the "executable paper").

This conference will be a boost for our growing Scientific Pythonic Community and an exciting beginning!

Why I am writing in English about a Spanish Conference? Because I want to spread the word about this conference as much as I can and because there are international speakers scheduled (and I would love to see more in the next conference!)

I hope to see you there!

Damián.

Facundo Batista: Actualizaciones

   Publicado: 2013-04-23 14:37:00


Estos días hice dos releases rápidos.

El primero fue de LauncherPosta, porque resulta que en Ubuntu Raring crasheaba mal. O sea, no tiraba un traceback: crasheaba.

¿Lo peor? Es un problema de la librería PyGtk, de cómo está implementado para Gtk 3. ¿Lo peor más peor de todos los peores? Es por diseño, y les parece bien que crashee a la mierda en lugar de tirar un traceback decente (miren el bug que abrí y lo que me respondieron).

En fin, esto refuerza lo que les decía que Gtk3 me gustaba menos y menos y me estoy pasando a Qt.

BTW, de LauncherPosta liberé la versión 1.0, con el casi único cambio de soportar mejor el toqueteo de la configuración del systray bajo Unity (un pedazo de código que luego les compartiré más separadamente).

El segundo release fue de Enjuewemela.

Hace rato que no sacaba una versión del jueguito. Es que aunque le había hecho un montón de correcciones, había un gran feature que estaba esperando: el replayer.

¿Qué es el replayer? Es un modo de ejecución de Enjuewemela que le decís que te reproduzca un juego anterior (le tenés que pasar el log que generó la jugada), y podés ir viendo exactamente el juego que hiciste, avanzando y retrocediendo con las flechas. Esto más que nada lo hice porque era necesario para poder detectar algunos crashes raros, y porque era divertido de hacer, :)

Los cambios más interesantes para esta versión 0.5, aparte de la habilidad de "re-jugar un log", son:

- Alienta cuando hay múltiples coincidencias
- Cambia el tablero cuando cambia de nivel
- Múltiple fondos
- Correcto manejo de los highscores
- Otras pequeñas mejoras y un montón de correcciones.

La verdad es que estoy un poco harto de Enjuewemela. Hay que ponerle un montón de laburo para "hacerlo más lindo" al juego, y la verdad es que "hacerlo lindo" no es algo que me divierta.

Así que creo que sacaré un gran último feature, y luego creo que lo paso a mantenimiento, porque tengo otros proyectos bastante más interesantes para empujar.

Ya los comentaré por este mismo canal. Stay tuned.

Facundo Batista: Migrando Encuentro a PyQt

   Publicado: 2013-04-17 20:59:00


Este no es un post sobre Encuentro precisamente, sino sobre la experiencia de migrar Encuentro a Qt.

O, mejor dicho, a PyQt. ¿Qué es PyQt? Sencillo: una capa de unión para poder usar Qt desde Python. ¿Y qué es Qt? Qt es una biblioteca multiplataforma para desarrollar aplicaciones con interfaz gráfica. En otras palabras, una biblioteca para hacer las ventanas, botones, y todo eso que arma la interfaz gráfica de un programa de escritorio.

Con esa descripciones no tendríamos diferencia entre PyQt/Qt y PyGtk/Gtk, que es lo que usaba Encuentro hasta ahora. Entonces, ¿por qué migrar?

Son varias las razones... pero principalmente porque empaquetar PyGtk en un .exe es un dolor de muelas, y eso llevó a que la última versión que corre en windows es la que no funciona porque cambió todo el backend web (cuando los videos pasaron de ser hosteados por Encuentro a estar en Conectate). En otras palabras: la última versión de Encuentro que corre en windows no sirve para nada, y básicamente es culpa de Gtk.

Otras razón de menor importancia es que no me gustó como Gtk evoluciona. El futuro del framework es Gtk3, y ya estuve tirando código para usarlo, y lo que usé me gustó menos que Gtk2, así que me pareció un buen momento de cambiar. Finalmente, es una buena excusa para aprender Qt, ;)

Qt

En fin. La migración ya está terminada, pude hacer en Qt todo lo que tenía que hacer en función de la interfaz de Encuentro. ¿Qué me pareció? Bueno, las sensaciones son varias.

Me gustó Qt, mucho más cuadradito, más pytónico especialmente en la versión 4 que es la que yo estoy usando. Aunque la mayoría del código es muy similar, hay varias cosas que son más sencillas que en Gtk, aunque no todas, y hay bordes que limar.

(En este punto quiero aclarar que en ningún punto usé Qt Creator, el constructor gráfico de interfaces, sino que hice todo todo a mano, lo cual me permitió meterme bien adentro del framework y aprender mucho de su estructura subyacente.)

Un ejemplo de borde sencillo: no se puede saber si una señal está conectada o no. Entonces, por ejemplo, yo tengo un botón que muta de función, y a veces tiene que tener una señal conectada, y a veces otra (para que al hacer click haga una cosa u otra; en particular en el contexto de Encuentro: que el botón dispare la descarga del episodio, o la reproducción). Cuando el contexto cambia y se hace la revisión del estado del botón, no puedo decirle que desconecte cualquier señal que tenga, o preguntar qué señal tiene y desconectarla, tengo que (a mano) guardar en algún lado la señal que había conectado antes para desconectarla y conectar la nueva que corresponda.

Un ejemplo de algo complicado de hacer en Qt (que en Gtk es trivial): QTreeWidget no soporta HTML en el texto. Esto es, la habilidad de insertar tags para cambiar el tipo de texto (en el caso de Encuentro, yo lo necesito para resaltar en amarillo el fondo de las letras que coinciden con lo que el usuario ingresó en el campo de filtrar). Finalmente lo pude hacer, adaptando un ejemplo que Roberto Alsina encontró en la web, pero lo hace más lento, le agrega pequeños glitches que aunque no me joden, no deberían estar, y me mete a mí código oscurísimo que no es ni cerca de fácil de debuguear.

Por último, la integración con Twisted no es trivial. Hay cosas que en Encuentro están hechas con Twisted que podrían hacerse con herramientas más propias de Qt, sí, pero en este caso de migración, ya estaban hechas en Twisted y mi idea era aprovecharlas. Pero tuve que meter en el proyecto todo un módulo de integración y levantar la aplicación y cerrarla de una manera no trivial (y que me costó tiempo y sudor hacer que funcione correctamente, especialmente la parte de cerrar la aplicación, porque tuve que apagar los hilos de Twisted a mano).

La conclusión es que Qt me gustó bastante, y aunque extraño algunas cositas de Gtk, seguramente mis nuevos proyectos estarán usando PyQt.

Roberto Alsina: Here's a very big gun, there's your foot: PHP support in Nikola

   Publicado: 2013-04-16 10:26:00

I am a very big proponent of static site generators. I would not have bothered writing Nikola otherwise. But there is always that feeling that maybe there is some little thing which is hard to implement, like a contact form.

And let's face it, the easiest way to solve some of those things is by sticking a few lines of PHP in your HTML.

So, if you really want to, you can do it. I think Nikola (github master) is the first static site generator that supports php code. Here's how:

  1. Add php to your page_compilers (because I will never put it there by default):

    post_compilers = {
        "rest": ('.txt', '.rst'),
        "markdown": ('.md', '.mdown', '.markdown'),
        "textile": ('.textile',),
        "txt2tags": ('.t2t',),
        "bbcode": ('.bb',),
        "wiki": ('.wiki',),
        "ipynb": ('.ipynb',),
        "html": ('.html', '.htm'),
        "php": ('.php'),
    }
    
  2. Add php posts or pages to your post_pages:

    post_pages = (
        ("posts/*.txt", "posts", "post.tmpl", True),
        ("posts/*.php", "posts", "post.tmpl", True),
        ("stories/*.txt", "stories", "story.tmpl", False),
        ("stories/*.php", "stories", "story.tmpl", False),
    )
    
  3. Create a php post:

    nikola new_post posts/foo.php
    
  4. Put php in there:

    <!--
    .. date: 2013/04/16 09:57:09
    .. title: php test
    .. slug: foo
    -->
    
    <?php
    Print "Hello, World!";
    ?>
    

Build the site as usual, and you should end up with a page with PHP extension, that has that PHP in the "content" area, so it will follow your site's theme. Of course you can't do things like add HTTP headers and such, but hey, read the title.

Roberto Alsina: Nikola version 5.4.4 is out!

   Publicado: 2013-04-15 11:04:00

Yes, version 5.4.4 of Nikola, my static site/blog generator is just published at the usual place, including the following improvements:

Features

  • New Japanese translation.
  • Nikola check exists with 1 if there is an error
  • New HIDE_UNTRANSLATED_POSTS option that ensures you don't have mixed-language pages (Issue #373)
  • New theme "site-planetoid" for use with the planetoid plugin.
  • New 'retired' tag for posts that should no longer be in feeds.

Bugfixes

  • Added post data as a uptodate check for mustache (Issue #456)
  • Rebuild post pages when the post's translation list changes (Issue #458)
  • Handle "-h" (Issue #460)
  • Added correct help for console command (Issue #460)
  • Escape twittercard data (Issue #452)
  • Added missing "twittercard" in story template
  • Added support for per-language tags (Issue #450)
  • Fix wrong path splitting (Issue #434)
  • Remember locale even when set_locale failes (Issue #446)
  • Decode path argument in new_post (Issue #442)
  • task_indexes had missing config dependencies (Issue #441)
  • Removed bogus links to slides assets that were removed
  • Compressed files were seen as unknown by "nikola check"
  • local search and mustache plugins must be disabled by default (Issue #437)
  • Avoid failure if there are no tags and USE_GZIP is enabled (Issue #439)
  • Fix aspect ratio detection in Vimeo videos (Issue #440)
  • Blogger importer was passing wrong options to "nikola init" (Issue #408)

Roberto Alsina: Olvidate del modo "incógnito", usá un browser descartable!

   Publicado: 2013-04-10 10:23:00

No es porque lo haya escrito yo (bueno, sí) pero si necesitás un browser "limpio" sin cookies, etc, para testear algo, podrías encontrar maneras peores que usar Devicenzo así:

rm -f ~/.config/ralsina/devicenzo.conf
curl https://devicenzo.googlecode.com/svn/trunk/devicenzo.py | python

La primera línea elimina toda la configuración, cookies, etc que puedas tener, y la segunda descarga la última versión (no te preocupes, tarda dos segundos) y la lanza.

Y voilá, un browser recién desembalado, basado en webkit, sin historia previa, ni cookies, ni configuración, y razonablemente feature-complete.

Note

Esto requiere que tengas python y PyQt instalado de antemano, por eso Devicenzo es tan chiquito.

Martín Gaitán: pip, apurate por favor

   Publicado: 2013-04-10 01:17:00

pip es una herramienta esencial para el trabajo diario de un programador python: es el manejador de paquetes de nuestro entorno de trabajo (¡virtual por favor!), con el que instalamos, actualizamos o eliminamos las dependencias de nuestro proyecto (y, recursivamente, las dependencias que estas pudieran tener).

Conceptualmente es similar a los manejadores de paquetes de sistema como apt-get, diferenciándose en que, por defecto, consulta cada vez a una base de datos online si el paquete solicitado existe y de dónde puede bajar la última versión o la específica que se haya pedido.

Responder "qué, cuál y de dónde" es una tarea lenta porque dicha base de datos no es más que una página html por cada paquete con links que funcionan como un índice (como este que pip debe parsear comparar y elegir la mejor opción para bajar (a veces incluso debe parsear la homepage del proyecto en busca de links de descarga, puaj!).

Por eso (y porque muchas veces la infraestructura se satura) el uso estándar de pip es lento. Pero hay algunas maneras de que lo sea menos. Veámoslas.

No bajes dos veces lo mismo

El funcionamiento básico de pip es instalar un paquete con pip install <paquete>: busca, baja e instala el paquete. El flag --download_cache=<path> evita repetir el paso del medio, cosa tediosa cuando tenemos que instalar frecuentemente (por ejemplo en distintos virtualenvs) la misma dependencia o cuando el uso de ancho de banda es limitado.

Por ejemplo instalamos por primera vez lxml y vemos cuanto tarda.

(test)tin@traful:~/lab/test$ time pip install lxml --download-cache=~/.pip_download
Downloading/unpacking lxml
  Downloading lxml-3.1.1.tar.gz (3.3MB): 3.3MB downloaded
  Storing download in cache at /home/tin/.pip_download_cache/https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2Fl%2Flxml%2Flxml-3.1.1.tar.gz
  Running setup.py egg_info for package lxml
    Building lxml version 3.1.1.

    [... compilación]


Successfully installed lxml
Cleaning up...

real    2m58.276s
user    0m38.822s
sys 0m0.676s
(test)tin@traful:~/lab/test$

¡3 minutos! Y eso que estoy en una conexión bastante rápida.

Tip

Cualquier flag que pip acepta en su linea de comando se puede configurar como una variable de entorno. Entonces podemos setear flag por defecto en nuestro .bashrc, por ejemplo.. code-block: bash

export PIP_DOWNLOAD_CACHE=~/.pip_download_cache

Pero sigamos: una vez cacheado, las siguientes veces que queramos instalar la misma versión de lxml no bajará el archivo de nuevo

(test2)tin@traful:~/lab/test2$ time pip install lxml --download-cache=~/.pip_download

Downloading/unpacking lxml
  Using download cache from /home/tin/.pip_download_cache/https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2Fl%2Flxml%2Flxml-3.1.1.tar.gz
  Running setup.py egg_info for package lxml
    Building lxml version 3.1.1.

    [... compilación]

Successfully installed lxml
Cleaning up...

real    2m30.624s
user    0m38.966s
sys 0m0.504s

Mejoró realmente poco. ¿que clase de caché es esta? Chusmeemos que hay en el directorio.

(test)tin@traful:~/lab/test$ ls ~/.pip_download_cache/ | grep lxml
https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2Fl%2Flxml%2Flxml-3.1.1.tar.gz
https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2Fl%2Flxml%2Flxml-3.1.1.tar.gz.content-type

¿Caché de urls? -download-cache no evita todo el laburo de averiguar de dónde bajar, sino simplemente no baja si la url resultante ya existe (como nombre de un archivo) en este directorio.

Lo explica simple Carl Meyer:

La función --download-cache no apunta a prevenir la búsqueda en la red del archivo correcto a bajar: todo lo que hace es guardarlo una vez que lo encontró. Si de verdad te interesa instalar tus depedencias desde tu compu (sin salir a la red) usá --download primero y luego --find-links (apuntando al path de descarga) con --no-index.

Una caché sin salir a la red

Sigamosle la corriente al bueno de @carljm:

(test3)tin@traful:~/lab/test3$ time pip install --download=~/.pip_packages lxml
Downloading/unpacking lxml
  Using download cache from /home/mgaitan/.pip_download_cache/https%3A%2F%2Fpypi.python.org%2Fpackages%2Fsource%2Fl%2Flxml%2Flxml-3.1.1.tar.gz
Saved /home/mgaitan/.pip_packages/lxml-3.1.1.tar.gz
    [...]

Successfully downloaded lxml
Cleaning up...

real    2m8.969s
user    0m1.008s
sys 0m0.136s

¡Uff, 2 minutos en copiar un archivo que ya tenía bajado! (evidentemente lo que demora mucho es averiguar la versión del archivo a bajar)

Tip

se puede inspeccionar el berenjenal de redirecciones y parseos que suceden hasta que pip da con el paquete lxml a bajar haciendo el comando más verborrágico con pip install lxml -vvv

En este caso, el caché es directamente el archivo:

(test)mgaitan@traful:~/lab/test$ ls ~/.pip_packages/ | grep lxml
lxml-3.1.1.tar.gz

Por suerte, una vez cacheado el paquete de esta manera no tendremos que consultar el índice online las siguientes veces.

(test3)mgaitan@traful:~/lab/test3$ time pip install --no-index --find-links=~/.pip_packages lxml
Ignoring indexes: https://pypi.python.org/simple/
Downloading/unpacking lxml
  Running setup.py egg_info for package lxml
    Building lxml version 3.1.1.

    [...]


Successfully installed lxml
Cleaning up...

real    0m38.944s
user    0m38.338s
sys 0m0.564s

Ok, ya va mejor.

Haciendo que la cosa vuele: no recompiles la rueda

pip 1.4 (en desarrollo) trae soporte integrado para el nuevo formato de paquetes wheel (superador del viejo egg y basado en los estándares actuales) que es muchísimo más rápido que instalar desde fuentes (sobre todo en casos que se debe compilar, como lxml)

Para usar wheel el paquete a bajar tiene que existir en dicho formato y todavía no abundan en PyPi asi que podemos armarlos localmente con el propio pip

pip wheel --wheel-dir=./pip_packages lxml

Eso es similar a usar --download pero además compila y empaqueta como un archivo .whl

Para que pip acepte instalar estos archivos hay que usar --use-wheel y para que los busque localmente haremos:

pip install --use-wheel --no-index --find-links=~/.pip_packages lxml

¡Lo que tardó menos de 2 decimas de segundo! Un speedup del 90mil veces respecto al primer y canónico pip install lxml

(test)tin@morochita:~/lab/test$ time pip install --use-wheel --no-index --find-links=. lxml
Ignoring indexes: https://pypi.python.org/simple/
Downloading/unpacking lxml
Installing collected packages: lxml
Successfully installed lxml
Cleaning up...

real    0m0.180s
user    0m0.152s
sys 0m0.024s

Asi que ya sabés: todos esos paquetes que instalás en cada entorno (quizas ipython, django, whatever) me los haces rodar para que pip vuele.

Roberto Alsina: Serbo-Croatian version of PyQt By Example!

   Publicado: 2013-04-08 10:14:00

A while ago I got an email from Anja Skrba asking me for permission to translate PyQt by Example into Serbo-Croatian.

And here it is all nice and translated. Lots of thanks to Anja for the hard work!

Roberto Alsina: Nikola 5.4.3 released!

   Publicado: 2013-04-07 13:05:00

I am thrilled to announce the release of version 5.4.3 of Nikola a static website/blog generator.

The changelog is pretty long, more information at the announcement

Have fun!

Share