Usando Twisted para hacer aplicaciones de escritorio no bloqueantes
-
Upload
martin-volpe -
Category
Technology
-
view
723 -
download
1
description
Transcript of Usando Twisted para hacer aplicaciones de escritorio no bloqueantes
Usando Twisted para hacer aplicaciones de escritorio no bloqueantes
Martín Volpe@martinvol
PyCon Argentina
Bernal, Buenos Aires, Argentina, Noviembre 2012
Versión online: http://volteck.net/development/twisted-escritorio-charla.rar
Aclaraciones:
• Para el objetivo de esta charla: Twisted = Asincrónico
• Twisted no es la única forma
• Los ejemplos van a ser triviales
• Los usuarios se desesperan fácil
• ¿Alguien programó en Assembler? (no se asusten!)
Problema:• La aplicación se congela (no necesariamente satura el CPU)
tú software
Objetivos de la charla:
• Entender porque las aplicaciones se bloquean
• Mostrar como Twisted se acopla a este problema
• Entender las diferentes herramientas que tiene Twisted para solucionarlos
• Entender que Twisted sólo parece magia, pero no lo es, ¡Es Python!
¿Por qué pasa esto?:
# esto!while True: stuff = wait_for_stuff(timeout)
if stuff: stuff.execute() run_timed_events()
#si esto tarda en llamarse, la interfaz se bloquea refresh_GUI() #esta función de por si, tambien bloquea
¡Es culpa del reactor! ok..., para, ¿Qué es un reactor? Un loop.
• El reactor de Gtk, Qt, Wx y TKinter se pueden resumir a algo parecido a esto.
• Los que hayan programado con PyGame seguramente reconozcan este snippet
¿Por qué pasa esto?:
$ python examples/1.py
Esperar eventos
Bucle del reactor
Ejecutar eventos
¿Por qué pasa esto?¡¡Es culpa del reactor!! ok..., pero, ¿Que es un reactor?
$ python examples/2.py
Bucle del reactor
Callback
Tu código
Toolkit
Twisted:• Nada mas ni nada menos que un FrameWork escrito en Python
• Incluye un reactor extendible
• Originalmente creado para NetWorking, feature que podemos seguir utilizando para aplicaciones gráficas
• Empezó a escribirse en 2002: es maduro y estable;
• Por esto mismo: no respeta PEP8
• Es MUY extenso
• Licenciado bajo MIT License
http://twistedmatrix.com
Dos conceptos
• The Hollywood principle: No nos llames, nosotros te llamamos (no tan crítico en aplicaciones de escritorio)
• Multitasking cooperativo: todos compartimos el reactor, nos comprometemos a liberarlo lo mas rápido posible
Ejemplos típicos:• Buscar un recurso de red: urllib, sockets, etc
• Bucles y algoritmos matemáticos
• Llamadas a base de datos
• Interactuar con hardware
• print() (!!!)
• tus funciones
Estos ejemplos pueden no bloquear en todas las oportunidades, hasta que bloquean.
Casi todo se puede implementar como función no bloqueante, lo que no implica que sea la manera mas difundida (u obvia)
$ python examples/3.py$ python examples/slow_server.py &
Una solución: ThreadsPuede servir:
• Poco overhead
• Comparte el namespace
Pero:
• Muy poco control sobre lo que se está ejecutando (no se pueden detener!)
• Interactuar con el GIL: http://blip.tv/carlfk/mindblowing-python-gil-2243379
Un ejemplo:http://python.org.ar/pyar/Recetario/Gui/Gtk/MultiThread
Otra solución: MultiprocessingPuede servir:
• Se puede detener la ejecución
• Utiliza múltiples cores (si el procesador es multi-core puede correr rápido)
Pero:
• Overhead, abre otro intérprete de Python completamente independiente
• Por eso mismo: no comparte namespace (hay que usar Queues o Pipes)
http://python.org.ar/pyar/Recetario/MultiprocessingYThreading
Twisted: multitarea colaborativaEn el 99% de los casos esas soluciones clásicas son aplicables, pero eso no quiere decir que sean las mas adecuadas
Tiempo
Tarea 1 Tarea 2 Tarea 3
Tarea 1
Tarea 2
Tarea 3
Deferred:Es un objeto que nos ayuda a controlar flujos de ejecución asincrónicos
Fuente de datos
Deferred
Callbacks
Errbacks
resultado error
Deferred:Es un objeto que nos ayuda a controlar flujos de ejecución asincrónicos
Loop Callbacks
otros deferreds pueden ejecutarse
Deferred:
Sincrónico:
try:resultado = get_data_bloqueante()resultado = procesa_datos(resultado)
except Exception, e:resultado = maneja_excepcion(e)
d = get_data()d.addCallback(procesa_datos)d.addErrback(maneja_excepcion)
Asincrónico:
Deferred:
Sincrónico:class X:
def get_data(self):# lanzo algo que eventualmente # llame a data_readyself.d = Deferred()return self.d
def data_ready(self, data):self.d.callback(data)
service = X()service.get_data().addCallbacks(callbacks)
Deferred:try:
guardar_bloqueante(unDato)except Exception, e:
print "Algo no anduvo."
d = guardar_no_bloqueante(unDato) # retorna enseguida
def handle(error):print "Algo no anduvo."
d.addErrback(handle)
TIP: Es importante encapsular el código
import guitoolkit
class Gui:def on_button_click(self, widget): ...def hide_entry1(self): ...
class App():def __init__(self, gui): ...def guardar(self): ...def conseguir_datos(self): ...
if __name__ == '__main__':gui_inst = Gui()App(gui_inst)guitoolkit.run()
Respetar el paradigma orientado a objetos nos ahorra dolores de cabeza:
Twisted entra en acción:
• Twisted es un framework diseñado para el desarrollo de aplicaciones web; es basado en eventos
• Incluye su propio reactor
• Este reactor puede ser acoplado con Gtk, Tkinter, PyUI, wxPython
• El reactor se puede acoplar a otros frameworks, como PyGame.
• Y Qt? Por problemas de licencias no está incluido con el código de twisted, pero se puede descargar por separado http://twistedmatrix.com/trac/wiki/QTReactor
Documentación sobre los diferentes reactors:http://twistedmatrix.com/documents/current/core/howto/choosing-reactor.html
Integrando con el toolkit gráfico:
from twisted.internet import gtk2reactor # varía dependiendo el toolkit a integrargtk2reactor.install()
# todo tu código# ...
from twisted.internet import reactor# arrancamos el reactor de twistedreactor.run()
• ¿Notaron que el código de gtk no cambio y no se rompió nada?
• Tambien hay que notar que sigue fallando en el mismo lugar, acordate: ¡Twisted no hace magia!
$ python examples/twisted/1.py
Herramientas que incluye Twisted: Estandariza muchas acciones comunes:
• reactor.callLater(segundos, funcion, *args, **kw), Llama a funcion despues de pasados tantos segundos
• reactor.callWhenRunning(funcion, *args, **kw), Llama a funcion cuando el reactor empieza a ejecutarse
• deferToThread(funcion, *args, **kwargs), Ejecuta funcion en otro thread y retorna un objeto Deferred
• @inlineCallbacks, transforma funciones comunes en funciones asincrónicas (para retornar hay que usar returnValue)
• Deferred.cancel(), cancelar una tarea en curso dentro de un deferred
• deferLater(reactor, segundos, funcion), ejecuta funcion pasados tantos segundos, y retorna un objeto Deferred
Y las API's de los diferentes toolkits siguen funcionando, Twisted actua como un plugin.
Twisted entra en acción:
Reimplementemos los ejemplos usando lo que ya sabemos:
$ python examples/twisted/2.1.py
$ python examples/twisted/2.2.py # detenible
$ python examples/twisted/3.1.py
$ python examples/twisted/3.2.py # detenible
Caso práctico: Donnees
• Tareas asincrónicas
• Servidor Web
• Base de datos
• Gráficos en tiempo real
• Threads implementados con Twisted
• Envío de e-mail y generación de reportes (podría haber usado twisted.mail.smtp.sendmail)
• Tomar datos del puerto serie
Repo: https://github.com/maritnvol/Donnees-Acquisition-Data-software
$ git clone [email protected]:maritnvol/Donnees-Acquisition- Data-software.git$ cd Donnees-Acquisition-Data-software/Donnees$ python tilapia.py
Fuentes:
• http://www.artima.com/weblogs/viewpost.jsp?thread=230001
• http://twistedmatrix.com/documents/current/core/howto/process.html
• http://www.juanjoconti.com.ar/2010/12/01/videos-de-las-charlas-en-junin
• http://python.org.ar/pyar/CharlasAbiertas2010/Twisted
• http://krondo.com/blog/?page_id=1327
• http://mumak.net/stuff/twisted-intro.html
¡¡Muchas Gracias!!
¿¿Preguntas??
Martín Volpe@martinvol
http://about.me/[email protected]
Happy Coding!