A raiz de la consulta de un colega, me ha llamado la atención el módulo binascii. Este módulo se encarga de la codificación y decodificación de cadenas binarias para su transmisión en mensajes de texto. Lo habitual es que sea usado por otros módulos como uu, base64 o binhex, por lo que no es nada frecuente ver su uso directo en una aplicación.

Sin embargo, binascii posee algunas funciones que pueden sernos bastante útiles a la hora de simplificar el empaquetado de datos que requieren determinadas APIs, en lugar de usar estructuras más complejas como array o struct. También se revela muy útil a la hora de crear batería de tests donde necesitemos chequear valores binarios.

Estructuras array y struct

Por ejemplo, imaginemos que nuestra API nos pide que empaquetemos un número entero como cuatro bytes. Antes de python3 no existía una forma para controlar el tamaño en bytes de un objeto sin tener que recurrir a alguna estructura especializada. Por ejemplo:

from array import array

def numpack(num):
    a=array('L')
    a.append(num)
    return a.tostring()[::-1]

n=numpack(0xffeeddcc)  # res: \xff\xee\xdd\xcc

En el resultado final ha hecho falta invertir el orden de los bytes debido a que nos estaba usando un orden “little-endian” para su codificación. El orden puede depender del sistema donde estemos trabajando, con lo que no es nada seguro usar este método. Es preferible el uso más especializado de struct donde tendremos algo más de control sobre éste y muchos otros aspectos:

from struct import pack

def numpack(num):
    return pack('!L', num)

n=numpack(0xffeeddcc)  # res: \xff\xee\xdd\xcc

Nota que en la cadena de formato que se pasa a pack() tiene un indicador '!' al principio, con el que indicamos que queremos una ordenación “network (=big-endian)”.

El proceso inverso es tan fácil como usar la función complementaria unpack:

unpack('!L', n)[0]

Codificando mensajes

Lo visto hasta ahora funciona bien cuando tenemos que interaccionar con una API que use tamaños fijos para los datos. Pero, ¿qué pasa cuando los datos son de longitud variable, por ejemplo, un mensaje largo de decenas de bytes? En el mejor de los casos, tendríamos que ir byte a byte, tal vez apoyándonos en array o struct para realizar las conversiones, algo a todas luces resulta bastante tedioso.

Como ya adelanté, el módulo binascii nos va a ayudar en este cometido, en concreto la función b2a_hex y su contraparte a2b_hex.

Por ejemplo, nos envían en un mensaje un entero codificado en multibyte. No sabemos si son 2, 4 u 8 bytes, o incluso podrían ser más bytes de tratarse de un BigInt (entero muy largo). Con binascii podríamos resolverlo así:

from binascii import b2a_hex, a2b_hex

num = int(b2a_hex(msg), 16)

Para el proceso contrario, codificar un entero en una cadena binaria, usaríamos a2b_hex:

h = "%x"%num
if len(h)%2 == 1:
    h = "0" + h

msg = a2b_hex(h)

Hemos tenido cuidado de que la cadena hexadecimal tenga longitud par ya que a2b_hex codifica siempre cada byte a partir de una pareja de dígitos hexadecimales.

Estudio codificaciones unicode

También es posible usar binascii para estudiar las codificaciones de cadenas unicode, lo que hace más sencillo crear tests automáticos para funciones que empleen unicode. Sin muchos adornos, haríamos algo así:

#-*- coding: utf-8 -*-

from binascii import b2a_hex, a2b_hex

cadena = u"Peón \N{BLACK CHESS PAWN}"

print b2a_hex(cadena.encode('utf-8'))

#res: 5065c3b36e20e2999f

Comparando el resultado obtenido con la cadena unicode, vemos que la ó acentuada se codifica en ‘utf-8′ como 0xc3b3, o que la figura de peón negro se codifica como 0xe2999f.

Si cambiamos la codificación por ‘utf-16′ obtenermos como resultado fffe50006500f3006e0020005f26. Además de ser más larga, vemos que se añade al principio fffe, que es lo que se denomina BOM, y que nos indica qué ordenación de bytes se ha usado en la codificación ('FEFF' para Big Endian / 'FFFE' para Little Endian). Con fffe nos indica concretamente que se ha usado la codificación ‘UTF-16 Little Endian’, con lo que tenemos los bytes invertidos para cada caracter codificado (ver más info en el artículo sobre BOM de la wikipedia).

De no desear que se nos añada la marca BOM, podríamos haber forzado la codificación mediante 'utf-16be' ó 'utf-16le' para Big Endian y Little Endian, respectivamente.

 

Últimamente he necesitado pasar algunos ficheros de una web a codificación utf-8, codificación de caracteres más acorde con lo que se lleva hoy en día. En sistemas linux es una labor que se puede hacer fácilmente con la utilidad iconv:

$ iconv -f cp850 -t utf8 <fichero_entrada.txt >fichero_salida.txt

Pero hay veces que es necesario realizar esta conversión en windows. Si tenemos instalado python, una forma rápida de hacerlo sería:

$ python -c "import sys,codecs;codecs.EncodedFile(sys.stdout,'latin-1','utf-8').writelines(sys.stdin)" <fichero_entrada.txt >fichero_salida.txt

…¡y todo en una sóla línea!1

Tan sólo puntualizar que esta conversión emplea iteradores, por lo que no tiene que ser un problema el tamaño del fichero de texto a convertir.


  1. Para ver más ejemplos de “one-liners” os recomiendo este artículo de Joe di Castro 

 

Algunas veces necesitamos obtener el último item de un iterador. Para ello se suele iterar hasta agotar el iterador:

for it in iterador:
    pass

last_item=it

Una alternativa que se ve bastante es convertir previamente el iterable en una lista:

last_item=list(iterador)[-1]

Tiene el incoveniente de gastar recursos inultilmente al crear una lista de la que sólo nos interesa su último elemento.

En stackoverflow se pueden ver algunas respuestas a este problema, pero ninguna me convence lo suficiente.

Aquí pongo mi solución, simple y elegante donde las haya:

last_item=max(enumerate(iterador))[1]
 

Itanium, un sistema ¿obsoleto?

Últimamente, algunos grandes de la informática como Microsoft, Oracle y RedHat han determinado que los sistemas Itanium han quedado obsoletos con lo que dejarán de darles soporte, aunque hace sólo unos pocos años estos sistemas de 64bits se ofertaban al mercado como el futuro de los sistemas servidores empresariales.

En este punto, me encuentro que tengo en mi trabajo algunos servidores Itanium II que, lejos de considerarlos obsoletos, me parecen perfectos para alojar en ellos algunos de los proyectos python desarrollados en plone o django. Con la reciente salida de la distribución Debian “Squeeze”, y con la ayuda de un alumno que vino a hacer sus prácticas con nosotros, me animé a sustituir el Linux RedHat que se había quedado sin mantenimiento por una la última versión ia64 de debian. Esta versión es algo más limitada en paquetes que las versiones para arquitecturas i686 y amd64, pero con un poco de esfuerzo es posible completar la instalación compilando paquetes a partir de los fuentes. Y puedo afirmar que ha sido todo un éxito. Vuelvo a tener un sistema potente, completo y, sobre todo, mucho más libre.

La idea de este artículo es contar cómo instalar y configurar, en debian para itanium, del cliente de oracle y el conector cx_Oracle para python.

Instalación cliente oracle

Lo primero es descargar desde la web de oracle del cliente. Para ello hay que descargar los siguiente paquetes para itanium, previo registro gratuito:

basic-10.2.0.4.0-linux-ia64.zip
sdk-10.2.0.4.0-linux-ia64.zip
sqlplus-10.2.0.4.0-linux-ia64.zip

jdbc-10.2.0.4.0-linux-ia64.zip
odbc-10.2.0.4.0-linux-ia64.zip

Los dos últimos son opcionales, pero siempre pueden venir bien guardarlos por si hacen falta en el futuro con alguna aplicación (el conector jdbc nos vendrá bien para usarlo con jython).

Se decomprimen estos paquetes en el mismo directorio y obtendremos un único directorio llamado:

instantclient_10_2

Movemos este directorio a un lugar adecuado, por ejemplo a:

/opt/oracle/instantclient_10_2

No olvidar darle permisos adecuados, sobre todo si queremos que el servidor apache (mod_wsgi) pueda acceder a él (puedo asegurar que se pierde mucho tiempo hasta que averiguas este fallo tan tonto):

# chmod +rx /opt/oracle/instantclient_10_2/

Cuando pasemos a compilar cx_Oracle, veremos algunos fallos por no ser capaz de encontrar algunas librerías compatidas. Para evitarlo, debemos crear algunos enlaces:

# cd /opt/oracle
# ln -s libclntsh.so.10.1 libclntsh.so
# ln -s libclntsh.so.10.1 libclntsh.dylib

Ahora tenemos que actualizar las referencias a la libreras compartidas. Creamos el fichero /etc/ld.so.conf.d/oracle.conf con la siguiente línea:

/opt/oracle/instantclient_10_2

Y actualizamos:

# ldconfig

Para comprobar que funciona bien, podemos probar la utilidad sqlplus a ver si conectamos. Esta utilidad viene dentro del directorio.

Para poder compilar el paquete cx_Oracle se necesita unas cuantas variables de entorno que meteremos en el.profile`:

    export ORACLE_HOME="/opt/oracle/instantclient_10_2"
    export DYLD_LIBRARY_PATH="$ORACLE_HOME"
    export SQLPATH="$ORACLE_HOME"
 
    export PATH="$PATH:$ORACLE_HOME"

Para instalar el paquete cx_Oracle, podemos instalar antes el paquete python-pip que nos ofrece la utilidad pip que nos hará más fácil la instalación:

# apt-get install python-pip
# pip install cx_Oracle

Con ésto se debería descargar, compilar e instalar cx_Oracle. Saldrán algunas advertencias que podemos ignorar. Si todo ha salido bien, podemos pasar a probar si podemos importar el módulo:

    # python
    Python 2.6.6 (r266:84292, Dec 27 2010, 21:05:55)
    [GCC 4.4.5] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import cx_Oracle
    >>>

Con el módulo instalado, lo he probado desde el “backend” de oracle para django y todo funciona a la perfección.

 

Puestos a explotar todas las posibilidades de la utilidad qtm, también es posible emplear esta utilidad para previsualizar y subir al blog artículos escritos en reStructuredText (abreviadamente ReST) en lugar de markdown que viene por defecto.

De la instalación de docutils obtendremos las herramientas básicas con la que crear documentación para python. Una de estas herramientas es rst2html, con la que podríamos convertir un artículo en ReST a html antes de subirlo. El problema es que esta conversión se realiza como documento completo, con las secciones <head> y <body> típicas de un documento html, lo que no va bien si queremos insertar la salida de este comando a la entrada de nuestro blog.

Como solución, podríamos crear una plantilla para rst2html que sólo contenga la parte del <body>, pero hay una alternativa mejor: usar el script rst2wp de Matthias Friedrich. Colocamos este script en un directorio apropiado (eg: /home/usuario/bin) y configuramos qtm de la siguiente manera:

qtm para ReST

Como ya comenté en anteriores artículos, para escribir en páginas web resulta más sencillo usar markdown o markdown extra, pero a veces no queda más remedio que emplear ReST cuando necesitamos escribir documentación técnica en determinados blogs. Con qtm tenemos una herramienta sencilla para estos cometidos.

 

Por defecto, qtm usa el comando markdown que se instala en linux en la ruta /usr/bin/markdown. Este comando acepta la sintáxis básica de markdown, insuficiente cuando estamos acostumbrados a usar la extensión markdown extra.

Con un pequeño truco, es posible usar markdown_extra con qtm. Instalando el módulo markdown para python (paquete python-markdown en ubuntu) obtenemos un comando alternativo llamado markdown_py que acepta numerosas extensiones para markdown, entre las que se incluye markdown_extra.

Para usar markdown_py en qtm, creamos el pequeño script que active la extensión extra en el parseo:

    #!/bin/bash

    exec markdown_py -x extra "$@"

Llamamos a este script markdown_extra, le asignamos permisos de ejecución y lo metemos en algún lugar apropiado como la carpeta bin de nuestro usuario linux. En la configuración de qtm, introducimos como ruta al comando markdown

/home/usuario/bin/markdown_extra

A partir de aquí podremos previsualizar nuestro código con markdown extra. También podríamos añadir otras extensiones como, por ejemplo, toc con la que podemos crear tablas de contenidos:

    #!/bin/bash

    exec markdown_py -x extra -x toc "$@"

Debemos asegurarnos que nuestro blog posea también esta extensión. Si no la tuviera, siempre tenemos la posibilidad de indicarle a qtm que convierta nuestra entrada a html antes de enviarla al blog.


Actualización

Para windows es necesario otro tipo de configuración. Una vez instalado el módulo python-markdown se debe crear un fichero .bat en C:\Python2.7\Scripts con el siguiente contenido:

    @"c:\python27\python.exe" "%1" -x extra -x toc "%2"

Si llamamos a este fichero markdown_extra.bat, la configuración de qtm quedaría de esta manera:

Preferencias de QTM

© 2011 Hyperreals *R Suffusion theme by Sayontan Sinha