Python


15
Jun 09

Mi problema con las listas de correo

Fíjense en el siguiente intercambio de correos:

  1. Pavel hace una pregunta
  2. Jose responde con un enlace a la documentación de la función en cuestión, junto con una explicación de lo que debe hacer
  3. Alexis responde con un ejemplo
  4. Carlos responde, al día siguiente exactamente lo mismo que lo que ha dicho Alexis hace unas meras horas.
  5. Manuel contesta con lo mismo que los dos que han contestado antes
  6. Y finalmente, Antonio se une a la fiesta, tres días después contestando con… lo mismo que los últimos tres.

¿Tan faltos de amor están estos tres perdedores que se ven llamados a contestar de manera repetida y sin originalidad a una pregunta tan insulsa y obvia como esta? No es la primera vez, ni será la última, que alguien hace una pregunta de respuesta simple y se abalanzan los que quieren poder fardar de que contestan preguntas en la lista de correo de Python.

Por cierto, la pregunta en cuestión es un ejemplo perfecto de cargo cult programming: cuando alguien copia y pega código sin saber bien como funciona, otro clásico infaltable de las listas de correo.


17
Oct 08

Cursores con MySQLdb en Python

Resulta que MySQLdb tiene la fea manía de devolver los resultados en una tupla (igual que PHP en realidad), en vez de devolver un dict que tiene más sentido. Esto pasa porque así se comporta el cursor que db.cursor() devuelve por defecto. Afortunadamente hay más cursores disponibles, entre ellos el DictCursor, pero yo siempre me olvido de como usarlos. Así que me anoto esto aquí para recordarlo.

Se puede hacer de dos maneras, una es por cursor individual:

import MySQLdb
db = MySQLdb.connect(...)
cursor = db.cursor(cursorclass=MySQLdb.cursors.DictCursor)

O se puede cambiar el tipo de cursor que db.cursor() devuelve por defecto:

import MySQLdb
db = MySQLdb.connect(...)
db.cursorclass = MySQLdb.cursors.DictCursor
cursor = db.cursor()

21
Mar 08

RSS de segunda mano

Llevo un par de días pensando en comprar un móvil nuevo. Actualmente tengo un Nokia 6230 en un estado deplorable y aunque me gusta mucho por su forma, tamaño ni demasiado pequeño ni grande y funcionalidad adecuada creo que es hora de invertir en uno nuevo.

He visto que una compañera de trabajo tiene un Nokia E60 y me ha gustado mucho. Tiene una pantalla grande, con una resolución bastante alta, corre Symbian (importante para probar Android en el futuro cercano) y tiene soporte para Wifi.

El punto es que estaba mirando si había alguno en segunda mano y aunque encontré algunos en Madrid no eran recientes y ya se habían vendido. Quise encontrar alguna manera de hacer que me avisase cuando hubiesen resultados nuevos pero no pude encontrar ni un mísero feed RSS.

Así que hice el mío. Hay una versión online y un modulo de Python para los que quieran instalarlo en su propio ordenador cuando inevitablemente los de segunda mano bloqueen la IP de mi servidor.


11
Mar 08

I <3 Python

$ python
Python 2.5.1 (r251:54863, Mar  7 2008, 04:10:12)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> S = [1, 2, 3, 4]
>>> any(x > 3 for x in S)
True
>>> any(x > 5 for x in S)
False
>>> all(x < 5 for x in S)
True
>>> all(x < 3 for x in S)
False

La cantidad de código que me podría haber ahorrado en esta última semana si hubiese sabido de estas dos maravillosas funciones antes. Ahora toca revisar y modificar.

Visto en The fate of reduce() in Python 3000


26
Nov 07

Como hacer un HEAD request en Python

Estuve el domingo unas cuantas horas buscando por internet como hacer esto y me estaba frustrando. Mi nuevo mini-proyecto secreto requiere monitorizar unas imágenes regularmente para ir actualizándolas a medida que van cambiando mediante un cron script que se ejecuta cada 15 minutos. Hay dos opciones:

  1. Bajarnos las imágenes todas las veces y comparar el md5sum para ver si han cambiado
  2. o hacer un pedido HEAD en vez de GET y hacer uso de las cabeceras Last-Modified y ETag.

¿Que cuales son las ventajas del punto dos? Lo más importante es que nos ahorramos ancho de banda nosotros y a los que le estemos bajando las imágenes, tampoco es cuestión de abusar del servidor de otra persona. Ojo que esto no aplica sólo a las imágenes, cualquier servidor moderno de hoy en día tiene soporte para estas cabeceras y las devolverá con cualquier tipo de archivo, incluso los que estén generados dinámicamente. Nos ahorramos ancho de banda porque sólo pedimos la cabecera del archivo que exceptuando casos extremos será mucho más pequeño que el resto del documento. Esto es especialmente útil para el contenido estático (js, imagenes, css, etc) y contenido que se baja regularmente como feeds RSS. Además de ahorrar ancho de banda también hacemos que el script vaya más rápido ya que no tiene que procesar todo el documento.

La idea es que hacemos un pedido HEAD inicial y guardamos el Last-Modified y ETag, luego en los subsiguientes pedidos enviamos pedidos GET con las cabeceras If-Modified-Since y If-None-Match. If-Modified-Since se usa con la fecha que guardamos anteriormente y If-None-Match con el ETag. Si ambos campos son los mismos en el archivo actual del lado del servidor entonces este debe responder con un ’304 Not Modified’

import StringIO
import pycurl

c = pycurl.Curl()
s = StringIO.StringIO()

c.setopt(pycurl.URL, 'http://theragingche.com/images/hand_made_cms.gif')
c.setopt(pycurl.HEADER, True) # estas dos lineas son las que importan
c.setopt(pycurl.NOBODY, True) # para hacer un pedido HEAD
c.setopt(pycurl.WRITEFUNCTION, s.write)

c.perform()
print s.getvalue()

Nos devuelve solo la cabecera:

$ python head.py
HTTP/1.1 200 OK
Date: Mon, 26 Nov 2007 12:29:50 GMT
Server: Apache/2.0.54 (Fedora)
Last-Modified: Thu, 16 Mar 2006 19:02:45 GMT
ETag: "303247c3-105-40f21566a7f40"
Accept-Ranges: bytes
Content-Length: 261
Connection: close
Content-Type: image/gif
X-Pad: avoid browser bug

Si luego hacemos:

import StringIO
import pycurl 

c = pycurl.Curl()
s = StringIO.StringIO()

c.setopt(pycurl.URL, 'http://theragingche.com/images/hand_made_cms.gif')

# especificamos las cabeceras
c.setopt(pycurl.HTTPHEADER,
    ['If-Modified-Since: Thu, 16 Mar 2006 19:02:45 GMT',
    'If-None-Match: "303247c3-105-40f21566a7f40"'])

c.setopt(pycurl.WRITEFUNCTION, s.write)
c.perform()

print c.getinfo(pycurl.RESPONSE_CODE)

Nos da:

$ python head.py
304