Categoría: Python

Asyncio – Python 3.4

Las entradas referentes a la librería asyncio son meramente apuntes y están hechas en un entorno con Python 3.4 . Por lo que es posible que existan errores de concepto.

Acerca de asyncio

La librería asyncio permite realizar operaciones de forma concurrente en un único hilo de ejecución, para ello proporciona un bucle basado en eventos. Este bucle permite gestionar de una forma eficiente operaciones E/S, cambios de contextos y eventos del sistema. Aunque asyncio proporciona un bucle por defecto desde la misma aplicación pero se pueden utilizar otras implementaciones.

La aplicación que utiliza este bucle registra en él el código a ejecutar, pudiendo determinar en qué momento será puesto en marcha, así como las condiciones bajo las que debe hacerlo. Se pueden registrar funciones propias de Python para ser ejecutadas, o bien las corutinas proporcionadas por asyncio.

Funcionamiento

El funcionamiento de este bucle se basa principalmente en un juego de quién controla el tiempo de ejecución disponible en él, el que por defecto se encuentra ejecutándose en el hilo principal. Esto quiere decir que se puede tener varios hilos de ejecución y en cada uno de ellos un bucle de asyncio realizando operaciones concurrentes.

Cuando el bucle se pone en marcha comienza a ejecutar las funciones registradas, cediéndoles a estas el control y esperando que en algún momento la que se esté ejecutando se lo devuelva, lo que le permitirá ejecutar otras funciones.

Componentes principales

El bucle basado en eventos (event loop)

Asyncio está basado en estos bucles, como se ha mencionado anteriormente este proporciona métodos para gestionar operaciones de E/S, la creación de clientes/servidores de varios tipos de comunicación y el lanzamiento de subprocesos.

Para obtener un bucle se puede hacer de dos formas:

  • asyncio.get_event_loop()
  • asyncio.new_event_loop()

El bucle tiene diferente modos de funcionamiento, que se pueden activar con los siguientes métodos.

  • bucle.run_forever():  Se ejecuta en un modo infinito. Se puede detener con el método stop(). Consultad la documentación, ya que este método no detiene el bucle inmediatamente, hay ciertos aspectos a tener en cuenta dependiendo de si el bucle está en ejecución. Si el bucle todavía NO se está ejecutando se puede utilizar close() que elimina las funciones  registradas en el bucle.
  • bucle.run_until_complete(Future): Ejecutarse hasta que se satisfaga una condición (un Future o derivado) y una vez satisfecha devuelve el resultado o la excepción producida.
    • Nota: Cuando el argumento es una corutina esta es envuelta para ser un Future.

Ejemplo:

import asyncio

# En este bucle no se hace nada.
# Sólo espera una señal para salir
# como control+c
bucle = asyncio.get_event_loop()
bucle.run_forever()

El bucle posee diferentes métodos para interactuar con él, pero los que se utilizan con frecuencia son los siguientes:

  • bucle.call_soon(función, *args): Registra una función/método en el bucle para ser ejecutado tan pronto como sea posible.
  • bucle.call_at(momento, función, *args): Programa una función para ser ejecutada en un momento determinado. Las corutinas no pueden ser llamadas mediante este método. Esta función devuelve un objeto llamado asyncio.Handle que permite cancelar la ejecución de la función incluso después de la puesta en marcha del bucle.
  • bucle.call_later(retraso, función, *args): Programa una función para ser llamada después del tiempo especificado (en segundos). Internamente llama call_at, por lo que no puede ejecutar una corutina. Por lo que si es necesario llamar a una corutina cada X tiempo esta debe ser programada desde una función normal.

Estos métodos permiten registrar funciones normales de Python, pero no se les puede pasar el valor de un argumento específico directamente, por lo que es necesario utilizar functools.partial() para ello.

Ejemplo con functools:

import asyncio
from functools import partial

def coche_info(modelo, matricula):
   msg = "Modelo del coche {0} con matrícula {1}"
   print (msg.format(modelo, matricula))

# Le damos un valor sólo al argumento "matricula" de la
# función.
mi_coche = partial(coche_info, matricula="1234")
bucle = asyncio.get_event_loop()

# Le decimos el modelo del coche, ya no es necesario especificar
# la matrícula.
bucle.call_soon(mi_coche, "VTR 23")
bucle.run_forever()

# Resultado de la ejecución:
# Modelo del coche VTR 23 con matrícula 1234

Ejemplo – Llamada simple

import asyncio

def llamame(bucle):
    print ("Holaa")
    # Detenemos el bucle
    bucle.stop()

# Obtenemos un bucle
bucle = asyncio.get_event_loop()

# Registramos la función llamame y 
# le pasamos como argumento el bucle
bucle.call_soon(llamame, bucle)

# Iniciamos el bucle en modo de 
# ejecutarse para siempre
bucle.run_forever()
# Salida. Ejecuta la función y se mantiene dentro del bucle.
Holaa

Aparte de estos métodos puede resultar interesante saber que internamente asyncio posee un reloj monótono propio para programar la llamada de las funciones, que indica un tiempo diferente al que devuelve time.time(). Este no se ve afectado por cambios en la hora del sistema.

Ejemplo – Mostrando el reloj interno cada 2 segundos

import asyncio

def llamame_luego(bucle):
    print ("Hola, mi reloj interno pone: {0}"
            .format(bucle.time()) )
   
    # Programamos una llamada en 2 segundos
    # a esta función
    bucle.call_at(bucle.time()+2, llamame_luego, bucle)

# Obtenemos un bucle
bucle = asyncio.get_event_loop()

# Registramos la función llamame y 
# le pasamos como argumento el bucle
bucle.call_soon(llamame_luego, bucle)

# Iniciamos el bucle en modo 
# "ejecutarse para siempre"
bucle.run_forever()
# Salida. El tiempo puede ser diferente !
Hola, mi reloj interno pone: 30359.646896135
Hola, mi reloj interno pone: 30361.648696301
Hola, mi reloj interno pone: 30363.65099661

Corutinas

El potencial de las corutinas básicamente es que pueden suspender su propia ejecución mientras espera a que suceda un evento (que finalice la lectura de un archivo, la respuesta de un cliente mediante sockets, etc).

Cuando una corutina suspende su ejecución le está devolviendo el control al bucle en el que está registrado, el cual continua con su ejecución cuando aquello que esperaba ha ocurrido.

La definición de una corutina se puede realizar por medio del decorador:    @asyncio.coroutine

Cuando se trabaja con las corutinas estas pueden suspender su ejecución llamando a: yield from otra_corutina() o Future

Si se utiliza un Future, que se explicará en el siguiente apartado, la función devolverá el control cuando se complete/satisfaga el Future. En caso de que el Future sea cancelado levantará una excepción en la función que lo espera (eso implica que deberás estar preparado para controlarla si en tu código participan acciones que lo podrían cancelar).

Ejemplo de corutina simple:

import asyncio

@asyncio.coroutine
def corutina():
    print ("Espero...")
    yield from asyncio.sleep(3) # Devolvemos el control al bucle
    print ("Ya no espero más")

bucle = asyncio.get_event_loop()
bucle.run_until_complete(corutina())

Al llamar a una corutina esta no se ejecuta sino que devuelve un generador, en el caso de una  corutina esta se podrá poner en marcha por tres medios:

  • Cuando es llamada desde otra corutina usando yield: yield from corutina()
  • Por el método create_task() del bucle.
    • Nota: El método asyncio.async(corutina()) está considerado como obsoleto desde Python 3.4.4.

Promesas de futuro (Future)

Los objetos Future representan algo que está pendiente de ser acabado, por lo que se utilizan para poner en marcha una función cuando se ha completado. El método create_task() registra la llamada a una corutina en el bucle.

Ejemplo de Future:

import asyncio

@asyncio.coroutine
def llenar_plato(plato):
    print ("Llenando el plato...")
    yield from asyncio.sleep(5) # Devolvemos el control al bucle
    plato.set_result("arroz con pollo") # Establecemos el resultado

@asyncio.coroutine
def comer(plato):
    print ("Esperando a que llenen el plato")
    yield from plato
    print ("Plato lleno. ¡A comer {0}!.".format(plato.result()))

bucle = asyncio.get_event_loop()
plato = asyncio.Future()

bucle.create_task(llenar_plato(plato))

# En cuanto acaben de llenar el plato el programa finalizará
bucle.run_until_complete(comer(plato))

# Resultado:
# Llenando el plato...
# Esperando a que llenen el plato
# Plato lleno. ¡A comer arroz con pollo!.

Tareas (Task)

Las tareas son funciones derivadas de los objetos Future por lo que otras corutinas pueden suspender su ejecución esperando su finalización. Como cualquier otra función esta puede devolver un valor o levantar una excepción (teniendo en cuenta que se puede originar por su cancelación). Se puede entender una tarea como un envoltorio encargado de ejecutar una corutina.

De hecho cuando se ha utilizado run_until_complete anteriormente con una corutina como argumento internamente se ha creado una tarea. Lo mismo ha sucedido cuando se ha utilizando el método create_task, el cual devuelve un objeto Task.

A pesar de derivar de un Future se comporta de una forma un tanto diferente cuando son canceladas (esto  lo explicaré en otra entrada). Por último NO hay que crear un objeto directamente usando asyncio.Task(corutina()) .

Notas acerca de esta entrada

En esta entrada se han omitido diversos aspectos relacionados con el manejo de excepciones o los resultados no esperados de algunos métodos que pueden llevar a largas horas de depuración de código.

Fuentes de referencia:

Anuncios
Bot de Telegram para Goear

Bot de Telegram para Goear

El día que cerraron Groveshark vi desaparecer con ella un inmensa cantidad de canciones que no llegaría a conocer ,solía pasar el rato haciendo clics en las que recomendaba y casi siempre me gustaban, desde mi punto de vista era un servicio cómodo de usar y con una gran variedad de canciones con buena calidad.

goear

Después de Groveshark conocí Goear y fue como ver un “oasis” entre los otros servicios (Spotify, Google Music,etc), la cantidad de canciones me pareció menor pero normalmente encontraba lo que buscaba y con una calidad aceptable. Si alguno se pregunta el motivo por el que prefiero usar páginas webs del estilo Goear o Groveshark para escuchar canciones es porque que simplemente me gusta el hecho de tener que abrir el navegador, esperar que cargue, ver algún elemento de la web fuera de su sitio(me hace gracia) y eso es todo.

Debido t_logoal bloqueo , que se puede saltar cambiando el DNS, cuando me encuentro fuera de casa y uso la conexión de datos no puedo acceder a las canciones de una forma sencilla, por lo que hice un bot para navegar por Goear desde Telegram sin importar la red a la que estuviera conectado.

 

Acerca del bot

Requisitos

Componentes

El bot se compone de tres partes (aparte de la API de telegram):

  • Una clase que representa al usuario.
  • Una clase que representa la búsqueda.
  • Otra clase que representa la conexión con una base de datos en Sqlite.

La clase usuario hereda las funciones y atributos de búsqueda, por la comodidad de cambiar las preferencias del usuario para futuras búsquedas. La base de datos se utiliza para guardar las preferencias de los usuarios(criterio, canciones por mensaje, etc) y poder restaurarlos si hay un apagón o algo parecido.

Funcionamiento

El funcionamiento es sencillo, pides la canción o el artista que buscas y te mostrará el resultado en un mensaje con opciones de navegación. En el resultado de la búsqueda se divide en trozos cada página, para facilitar la navegación.

Buscando una canción
Haciendo una búsqueda.

Si quieres aplicar un criterio de ordenación envías el comando /criterios para escoger uno.

Escogiendo un criterio
Criterios de ordenación disponibles (los de Goear).

Además puedes cambiar la cantidad de canciones que pueden aparecer en un mensaje con el comando /cantidad X , el cambio actualizará la última búsqueda.

Canciones por mensaje cambiada
Cambio en la cantidad de canciones por mensaje a 3.

Después de varios mensajes de configuración es posible que el resultado de la búsqueda que hiciste se quede arriba, puedes volver a solicitar tu última búsqueda con el comando /ultima_busqueda.

Última búsqueda
Reenvío de la última búsqueda realizada.

Si no sabes que configuración tienes puedes pedirla con el comando /config .

Recibiendo la configuración actual.
Mostrando configuración del cliente.

Aunque también se puede usar para obtener las canciones de Goear pasando el identificador de la canción, en forma de comando y el prefijo risa, por ejemplo: /risa_1e1a96f .El enlace de la canción lo hago más pequeño usando tinyurl, pero se puede usar otro (o ninguno).

Este prefijo lo podéis cambiar en la expresión regular. Otra opción es modificar el código para que tome como canción un comando que no corresponda a ningún criterio,es cuestión de gustos.

Los enlaces de Goear son temporales, por lo que después de un tiempo dejan de funcionar, creo que dejan de hacerlo después de media hora.

En caso de que la versión de escritorio de Goear no funcione el programa usará la versión móvil que no proporciona datos sobre la canción, a pesar de que se podrá seguir utilizando los criterios y funcionaran.

Información de la versión movil de Goear
El bot usa la versión móvil de Goear

Recomiendo que ejecutéis vuestro propio bot con el código que os facilito o el que hagáis partiendo del mío, ya que el mío no siempre estará disponible y mi torpe conexión a Internet puede hacerle perder la cabeza a cualquiera. El argumento para ejecutar el bot es el token proporcionado por el bot father de Telegram: python bot_goear.py TOKEN

Antes de finalizar la entrada me gustaría agradecer el servicio prestado por Goear durante estos años. Me parece una pena el bloqueo impuesto pero si con esto puedo ayudar a otros a seguir disfrutando del servicio estaré satisfecho.

El código está disponible en BitBucket: enlace. Mi bot se llama @telegoear Podéis dejar cualquier duda o sugerencia en los comentarios.

De https://pixabay.com/en/users/Alexas_Fotos-686414/

Por último quiero decir que puedes usar el bot para “regalar” una canción a alguien ya que no podrá saber de que canción se trata hasta que la escuche.

Resolviendo captchas

Los captchas no sólo se utilizan para protegernos del spam sino que además supone una medida de seguridad que proporciona al usuario mayor confianza al visitar la página web, porqué cree que aquello en lo que está invirtiendo su tiempo o incluso su dinero no va a irse por el desagüe debido a unos bots.

Estas últimas semanas me he interesado por los bitcoins y he estado visitando algunas páginas webs que por ver una cierta cantidad de anuncios te proporcionaban una pequeña cantidad de bitcoins,unos cinco u ocho céntimos, por ver anuncios de 1 minuto o incluso 2 por una cantidad tan pequeña que apenas se podía ver en el monedero.

Me pregunté si las personas que pagaban por poner su anuncio en esas páginas de verdad creían que su dinero iba a ser protegido por un captcha y otros métodos de verificación tan simples como utilizar la cabecera de HTTP Referer para validar los formularios contra los bots. En ocasiones es posible que el motor de reconocimiento óptico no sea capaz de reconocer el captcha una vez eliminado el ruido, pero existen otros métodos más sencillos para identificar de que carácter se trata.

Algunas de las técnicas que he utilizado para identificar los caracteres que no han podido ser reconocidos correctamente por el motor ocr tesseract:

  • Calcular el peso de cada carácter en píxeles y compararlos con el peso que tiene cada carácter guardados en un base de datos.
  • Contar el número de coincidencias por posición (x,y).
  • Utilizar el ruido para recomponer el carácter.
  • Usar los errores del motor ocr para identificar los caracteres

Para eliminar el ruido en el siguiente captcha fue suficiente con seleccionarlos por la intensidad de cada píxel:

inDespués de eliminar el ruido y el borde:

outEl motor tesseract no podía reconocer correctamente todos los caracteres así que tuve que contar el número de coincidencias por posición (recogí las posiciones de cada número y después las guarde en una lista), con esto fue más que suficiente para identificar cada carácter.

Para los que nos estamos iniciando en esto de bitcoin acostumbramos a utilizar páginas PTC (Paid-to-click) que pagan por ver anuncios, una de las más conocidas es Bitvisitor.com que tiene un captcha que aparentemente es seguro, incluye rayas del mismo color que las letras lo que hace más difícil extraer los caracteres, pero se pueden extraer parcialmente y recomponer cada carácter y pasarlo por el tesseract.

a

Después de recomponer el captcha:

outComo se puede ver no ha servido de nada poner rayas xD. Las confusiones constantes de tesseract con ciertos caracteres se pueden utilizar para devolver el captcha correcto.

Los captchas son un buen método para proteger los sitios web de los bots pero deben ser robustos de lo contrario no sirven para nada. Por el tipo de captcha que utiliza Bitvisitor prefiero no pagar para poner un anuncio ahí. He escrito este post sólo para darle un toque de atención a los que van a invertir en esa página.

Letras sincronizadas en Banshee

He visto programas que te permiten ver la letra de tus canciones favoritas sincronizadas en tu escritorio como osd-lyrics, pero ninguno de los que vi me gustó así que me puse a hacer mi propia versión de este tipo de programas, pero en Python y para Banshee(es el único que utilizo).

La idea de usar mi propia versión me permitiría tener más control sobre el programa en caso de errores y actualizaciones en lyrdb.com o Banshee.

(Necesita pynotify para funcionar,probado en Xubuntu).

LRC Banshee

Descargar de github: LRC Banshee

Captura de pantalla-1

En caso de no encontrar la letra intentará descargarla de lyrdb.com, todas las canciones del artista que encuentre, después recargará la BD – ineficiente, pero es algo que cambiaré en la siguiente versión.

Captura de pantalla-2

Para iniciar LRC Banshee tienes que ejecutarlo por la terminal con la siguiente orden: python main.py

El programa se cierra automáticamente al cerrar Banshee o puedes pararlo desde la terminal. Puedes poner tus propios archivos en la carpeta lrc que se creará automáticamente en el primer inicio.

Si antes de empezar a usarlo quieres tener algunos archivos lrc(algunos de ellos arreglados) puedes descargar este archivo zip con más de 2000 archivos lrc: LRC – archivos.zip una vez descargado sólo hay que extraer la carpeta lrc en el directorio de trabajo de LRC Banshee.

Lenguajes de programación

http://programming-motherfucker.com/
http://programming-motherfucker.com/

Hace unos días estuve buscando un manual de programación de java,Perl y Ruby. Después de buscar y no encontrar manuales en español que me gustaran opté por buscar un manual en inglés y que lo hiciera por el camino difícil y me encontré con esta página: Programming motherfucker. El nombre no es que sea muy bonito, pero tiene buenos manuales.

SQLite3 en python

Hay muchas maneras de guardar información en Python, en ficheros csv, con la libreria pickle, xml, json,etc. Pero si la cantidad de datos que quieres almacenar es muy grande es posible que te interese más una base de datos, pero si los datos que vas a almacenar no los vas a compartir con nadie seguramente te interese más una base de datos local, por este motivo sqlite es una buena opción para estos casos.

Requisitos:

  • Conocimientos básicos de SQL.

No es necesaria ninguna librería extra, Python ya incluye sqlite3 por defecto.Antes de empezar a usar sqlite debes crear una base de datos,para crearla es suficiente con conectarse a ella y en caso de no existir se creará automáticamente.

import sqlite3

database = sqlite3.connect("mi_db.db")

Las sentencias de sqlite se ejecutan a través de un cursor, por ejemplo crear una tabla en la BD:

import sqlite3

database = sqlite3.connect("mi_db.db")
cursor = database.cursor()

sentencia = "CREATE TABLE clientes (dni TEXT, nombre TEXT, apellido TEXT, PRIMARY KEY(dni))"
cursor.execute(sentencia)

database.commit()

cursor.close()
database.close()

Para aplicar los cambios en la BD es necesario usar commit(), después sólo queda cerrar el cursor y la BD.

Insertar datos en la tabla cliente:

Si ya tenemos creada la tabla en la BD ahora sólo queda insertar datos de la misma manera en que lo haríamos en SQL:

cursor.execute("INSERT INTO clientes (dni,nombre,apellido) VALUES ('91192984P','Pol','Naranja')")

Hay otra manera de pasar los valores a esta sentencia que consiste en utilizar el signo ?, que previene los ataques de inyección SQL.

dni = "91192984P"
nombre = "Pol"
apellido = "Naranja"

cursor.execute("INSERT INTO clientes (dni,nombre,apellido) VALUES (?,?,?)",(dni,nombre,apellido))

Ver los datos de la tabla

Cuando queremos ver todos los datos de la tabla ejecutamos la sentencia SQL y después podemos elegir si quedarnos con el primer resultado o con todo lo que ha devuelto la selección( o búsqueda).

Los datos son devueltos en forma de tupla, que representan las filas de las tablas, y una lista de tuplas en caso de existir más de una fila en la tabla:

sentencia = "SELECT * FROM clientes"
cursor.execute(sentencia)

#Guardar el primer resultado
resultado = cursor.fetchone()

#Mostramos el resultado
print resultado

#Salida: (u'91192984P', u'Pol', u'Naranja')

#Insertamos una entrada más
cursor.execute("INSERT INTO clientes (dni,nombre,apellido) VALUES ('31192984P','Pepe','Manzana')")

#Volvemos a ejecutar la petición
cursor.execute(sentencia)

#En este caso obtenemos todas las filas de la tabla
resultado = cursor.fetchall()
print resultado
#Salida: [(u'91192984P', u'Pol', u'Naranja'), (u'31192984P', u'Pepe', u'Manzana')]

Estos resultados se puede filtrar con WHERE para obtener unos datos determinados de un cliente, en este caso también podemos pasar como parámetro el nombre de la columna que nos interesa como si fuera una cadena, pero con una ligera diferencia, en lugar de usar el signo ? para pasar el nombre de la columna, tendremos que hacer la sustitución en la sentencia como lo haríamos con una cadena en Python. En caso de querer pasar un único valor para sustituir ? se debe poner una coma al final para que el parametro se considere una tupla, que es lo que espera execute: (dni, )

columna = "apellido"
dni = "91192984P"

cursor.execute("SELECT %s FROM clientes WHERE dni=?" % (columna), (dni,))

Es necesario reemplazar la columna como si fuera una cadena en Python debido a que al ser pasados  a través de sqlite los valores de las variables pasan como lo que son, si es de tipo string pasarán como string, es decir no hay una conversión, si por ejemplo la variable columna se hubiera sido pasada con ?, lo que vería SQL, sería esto:

SELECT ‘apellido’ FROM clientes …

Por lo que la consulta no funcionaria.

Cuando en realidad debería quedar como esto:

SELECT apellido FROM clientes…

Eliminar los datos de una tabla en sqlite

En sqlite3 a diferencia de SQL que se utiliza la sentencia: TRUNCATE TABLE nombre_tabla. Sqlite no utiliza esta sentencia sino que utiliza DELETE FROM nombre_tabla.

cursor.execute("DELETE FROM clientes")

Notas

  • Recuerda utilizar commit() para aplicar los cambios que hayas hecho en la BD.
  • Recuerda cerrar el cursor y la BD

Editar tags de canciones en Python

Continuando con la entrada: acoustid fingerprint. Voy a explicar que se necesita para leer las etiquetas de los ficheros mp3 y a la vez corregirlas.

Requisitos:

  • Descargar e instalar la librería eyed3, que nos permitirá visualizar y editar las etiquetas de las canciones.(puedes usar pip, si usas Linux)

Primer uso:

Obtener información de un fichero mp3 es muy fácil con eyed3:

import eyed3

#Path es la ruta del fichero

cancion = eyed3.load(path)

print 'Titulo: ' cancion.tag.title
print 'Artist': ' cancion.tag.artist

Modificar las etiquetas es tan fácil como obtenerlas:

import eyed3

#Path es la ruta del fichero

cancion = eyed3.load(path)

cancion.tag.title="Cualquier valor"
cancion.tag.artist="Cualquier otro"

cancion.tag.save()

Ahora si queremos identificar canciones desconocidas podemos utilizar pyacoustid, un servicio de identificación de canciones gratuito.

import eyed3,acoustid,sys

if len(sys.argv) > 1:
   path = sys.argv[1]
   print path
else:
   print 'Tienes que proporcionar una ruta'
   sys.exit(1)

#Path es la ruta de la canción

api_key='TU API KEY'

#Buscamos con acoustid el nombre y el artista,necesitas una api key para esto
datos_correctos = list(acoustid.match(api_key,path))

if datos_correctos:
   datos_correctos = datos_correctos[0][-2:]
   titulo = datos_correctos[0]
   artista = datos_correctos[1]

   print 'Title',titulo
   print 'Artist',artista

   #Abrimos el archivo
   cancion = eyed3.load(path)
   cancion.tag.title=titulo
   cancion.tag.artist=artista

   #Aplicamos cambios
   cancion.tag.save()

else:
   print "No se han encontrado datos"

Con esto y usando os.walk podréis etiquetar correctamente vuestra biblioteca multimedia.