tags/twistedStyXman's globhttp://grulicueva.homelinux.net/~mdione/glob//tags/twisted/StyXman's globikiwiki2009-04-08T04:27:04Zpycamp-2009http://grulicueva.homelinux.net/~mdione/glob//posts/pycamp-2009/2009-04-08T04:27:04Z2009-04-08T04:27:04Z
<p>Acaba de terminar la edición 2009 de pyCamp. Esta vez vinieron
cerca de 40 personas, lo cual hizo que hubiera más proyectos dando
vueltas y mas gente en los proyectos. Fueron 4 días fantásticos
llenos de ideas, código, reuniones, juegos, algo de alcohol y mucho
mas. A diferencia del año pasado, esta vez vienieron algunos
audaces con familia, no sé cómo les habrá ido.</p>
<p>Este año estuve mucho mas enganchado. El primer día hicimos un
schedule cuasi definitivo y en el momento se me ocurrió hacer cosas
con Fuse y Python. Cuando tocó el slot, di una charla de cómo
funciona Fuse y algunas puntas de cómo implementar file systems con
él. Al final del evento yo había terminado el wrapper que venía
haciendo hace unas semanas (ok, ok, falta <code>statvfs</code>) y
<a href="http://perrito666.com.ar">perrito</a> se hizo un
filesystem para acceder los iPod. Lucio me hizo prometer ver cómo
combinar Fuse async con Twisted. También le estuve explicando
<code>ctypes</code> al Polako, con lo que creo que terminé de
entender el módulo y me ayudó a entender algunas cosas que había
hecho para el wrapper.</p>
<p>También estuve en el diseño y (re)implementación del bot de irc.
En apenas 2 días y medio ya tenemos el core y unos cuantos plugins,
y hay varios desarrolladores haciendo mas. Sólo faltan implementar
pedezos de infraestructura, sobre todo la parte de bases de datos,
pero me veo metiendo un par de plugins mas y ponerla en producción
muy muy pronto (en relaidad perrito le va a dar hosting). También
fue una oportunidad para (re)aprender Twisted, y enterarse de cosas
como que <a href=
"http://twistedmatrix.com/projects/core/documentation/howto/gendefer.html#auto2">
no podés hacer asincrónico un proceso sincrónico</a>, y de aprender
de boca de Guillo cómo usar <code>bzr</code> para laburar entre los
6 u 8 que metíamos código.</p>
<p>También estuve renegando los dos primeros días con el applet de
batería de KDE4. Terminé encontrando (un bug en
Solid)[https://bugs.kde.org/show_bug.cgi?id=187600] y aprendiendo
detalles sobre Hal, D-Bus, algunos bastante oscuros y bizarros. Al
mismo tiempo estuve viendo cómo se comportan los algoritmos de
recarga de batería y de estimación de los tiempos de descarga y de
descarga. Resulta que cuando está terminando de cargar se empieza a
estirar el tiempo y los últimos 5 minutos pueden termiar siendo
20.</p>
<p>Estuvo genial poder conocer más gente y de volver a ver algunas
caras conocidas (hace rato que no estaba en un evento de alguna
comunidad). Entre los nuevos encontré a gente de <a href=
"http://kde.org.ar">Kde-ar</a> como Leo u otros jugando con PyQt.
Me encantó volver a sentir que programaba, ver unos proyectos
arrancar y otros continuar a velocidades de la hostia, con features
apareciendo como hongos y bugs desapareciendo como... bueno, no es
una buena fecha para hablar de desapariciones :|</p>
<p>El último sprint estuvo genial; monitoreen la lista y/o el canal
para enterarse de los resultados ;-)</p>
<p><a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../python/">python</a> <span class=
"selflink">twisted</span> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../bazaar/">bazaar</a> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../pyar/">pyar</a> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../kde/">kde</a></p>
twisted-RPChttp://grulicueva.homelinux.net/~mdione/glob//posts/twisted-RPC/2009-01-22T04:24:04Z2008-07-30T02:03:15Z
<p>Otra de las grandes cosas de Twisted es que tiene varias
opciones de RPC. De éstas, la que me parece más pythonesca es la
que dieron en llamar <a href=
"http://twistedmatrix.com/projects/core/documentation/howto/pb-intro.html">
Perspective Broker</a>. Con esta parte del framework sólo tenemos
que heredar de un par de clases, usar un par de factories ya listas
para usar, y ponerle <code>remote_</code> a los métodos que
queremos exportar:</p>
<pre>
<code>from twisted.spread import pb
from twisted.internet import reactor, defer
class MyServer (pb.Root):
# ...
def remote_hello (self, greeting):
print "client says %s" % greeting
return "why, hello there!"
s= MyServer ()
reactor.listenTCP (port, pb.PBServerFactory (s))
reactor.run ()
</code>
</pre>
<p>Éste es un server que exporta un método remoto
<code>hello</code>. El cliente no es mucho más difícil:</p>
<pre>
<code>def answered (answer):
print "server said %s" % answer
def connected (root):
root.callRemote ('hello', 'hi server!').addCallback (answered)
factory= pb.PBClientFactory ()
reactor.connectTCP (name, port, factory)
factory.getRootObject ().addCallback (connected)
</code>
</pre>
<p>Lo primero que tenemos que pedir antes de poder hacer nada es el
"objeto raíz". Éste es el objeto que nos va a permitir acceder a
todos los servicios remotos ofrecidos por el server. Este objecto
es el <code>s</code> que creamos en el server y que pasamos como
objeto raíz a la <code>pb.PBServerFactory</code>.
<code>getRootObject()</code> nos devuelve un
<code>Deferred</code>.</p>
<p>Una vez obtenido el objeto raíz, podemos empezar a llamarle
métodos remotos. Notar que <code>callRemote()</code> recibe el
nombre del método remoto como un string, y el resto de los
parámetros que pasamos son los parámetros con los que va a ser
llamado el método remoto en el server. Como es de esperarse, una
llamada a un método remoto no vuelve inmediatamente, sino que nos
da también un <code>Deferred</code>.</p>
<p>Ahora bien, vemos que los <code>Deferred</code>s son la estrella
del framework, y que los usamos en todos lados... ¿Qué pasa cuando
un método remoto no puede devolver inmediatamente el resultado?
Pues, aunque parezca raro y obvio al mismo tiempo, ¡devuelve un
<code>Deferred</code>!:</p>
<pre>
<code> # ... en MyServer
def bye (self):
return defer.succeed (True)
</code>
</pre>
<p>Acá estoy usando <code>defer.suceed()</code>, que es una función
que devuelve un <code>Deferred</code> con su valor ya disponible.
Esto es útil para cuando en algunos casos podemos calcular el valor
de la respuesta ya, pero en otros casos los tenemos que ir a buscar
en otro lado. También existe <code>defer.fail()</code>.</p>
<p>Ahora bien, acá entra un poco la magia de Twisted. Es obvio que
este <code>Deferred</code> no es el que es devuelto por
<code>callRemote()</code>, porque ése es el de haber llamado a un
método remoto, no el que devuelve ese método por no tener la
respuesta inmediatamente. Lo que no es tan obvio es que este
<code>Deferred</code> nunca viaja por la red. Perspective Broker se
da cuenta de que el valor no está disponible aún y por lo tanto no
retorna nada por la red. Esto es posible porque del otro lado ya
hay un <code>Deferred</code> "esperando". Cuando el resultado esté
disponible en el server, podemos llamar al método
<code>callback()</code> de la promesa con el valor calculado y
recién entonces se manda un mensaje por la red y se resuelve la
promesa del lado del cliente:</p>
<pre>
<code> # ... MyServer
mxCalc= relaymanager.MXCalculator ()
def remote_resolveMX (self, hostname):
promise= defer.Deferred ()
mxCalc.getMX (hostname).addCalback (self.resolvedMX, promise)
return promise
def resolvedMX (self, mxRecord, promise):
if mxRecord is not None:
answer= mwRecord.name
else:
answer= None
promise.callback (answer)
</code>
</pre>
<p>Acá estoy usando otro detalle que creo que no había presentado
antes: estoy llamando a <code>addCallback()</code> no sólo con un
callback, sino que le agrego otro parámetro, una referencia a la
promesa que devolví. Cuando <code>getMX()</code> resuelve su valor,
el callback es llamado con ese resultado como primer parámetro y
los otros parámetros que pasé a <code>addCallback()</code> después.
Esto me permite juntar el resiltado con la promesa devuelta
anteriormente.</p>
<p>Hay varias cosas más en el PB. Hay objetos que se pueden
referenciar remotamente, que se pueden copiar (hay dos tipos, pero
aún no les agarro la mano) y un par de cosas más. Además, Twisted
permite RPC usando XML-RPC (buzzword!) y también acceso a/servido
de webservices (buzzword!) a través de SOAP y REST (buzzword,
buzzword!).</p>
<p>Vamos viendo que Twisted tiene muchas cosas pensadas, y que los
conceptos que maneja no son muchos ni muy complicados. Si puedo
decir que en la forma en que se van 'partiendo' las soluciones en
métodos (estados) no ayuda a la legibilidad posterior del código.
Por ejemplo, vean cómo empezamos a tener métodos que representan
estados y que son varios para algo que de otra forma nos parecería
más sencillo:</p>
<pre>
<code> # ... MyServer
def remote_resolveMX (self, hostname):
return mxCalc.getMX (hostname)
# ...
# ... cliente
root= factory.getRootObject ()
mx= root.callRemote ('resolveMX', 'decode.com.ar')
# mandar un mail a pyar...
</code>
</pre>
<p>Tengo algunas ideas de cómo simplificar estas cosas. Básicamente
se basa en partir el conjunto de máquinas de estados representado
en una sola clase a una clase maestra que coordina todo y varias
clasesitas que manipulan las distintas maquinitas de estados.
Después les cuento bien.</p>
<p><span class="selflink">twisted</span> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../python/">python</a></p>
twismtpy-entrega-remotahttp://grulicueva.homelinux.net/~mdione/glob//posts/twismtpy-entrega-remota/2009-01-22T04:24:04Z2008-07-10T20:40:10Z
<p>Hoy vamos a ver cómo hacer entrega remota de un mail. Como somos
un server que recibió un mail que tiene que ser entregado en otro,
hay una serie de pasos que tenemos que hacer. Empecemos con una
implementación de <code>t.m.s.IMessage</code> que manda a un
smarthost:</p>
<pre>
<code>class Relay (object):
implements (smtp.IMessage)
def __init__ (self, router, user):
# select smarthost based on src domain
self.user= user
self.smarthost= 'our.smarthost.com'
self.lines= []
self.eom= False
self.router= router
def lineReceived (self, line):
self.lines.append (line)
def eomReceived (self):
return self.send ()
def send (self, mxRecord=None):
sender= smtp.sendmail (self.smarthost, self.user.orig,
[self.user.dest], '\n'.join (self.lines), '')
sender.addCallback (self.sendComplete)
return sender
def sendComplete (self, *data):
del self.lines
return data
def connectionLost (self):
del self.lines
</code>
</pre>
<p>Una cosa que no expliqué en el post anterior es el valor
devuelto por <code>eomReceived()</code>. En ese caso era
<code>return self.mailbox.appendMessage (messageData)</code>; en
éste, después de un par de vueltas, es el resultado de
<code>smtp.sendmail()</code>. Lo que estamos devolviendo es un
<code>Deferred</code>.</p>
<p>Los <code>Deferred</code>s son una parte importante de Twisted.
Son básicamente una promesa de que en algún momento va a haber un
valor disponible para devolver, pero que mientras le vamos dadndo
esto como para que tenga. El truco es luego conectar con esa
promesa nuestros callbacks llamando a <code>addCallback()</code>.
Esos callbacks van a ser llamados cuando el valor esté disponible.
También se pueden agregar errbacks, que son callbacks que son
llamados cuando la operación que pedimos tuvo un error (típicamente
una excepción).</p>
<p>Eso es exactamente lo que estamos haciendo en
<code>send()</code>. <code>t.m.s.sendmail()</code> nos devuelve un
<code>Deferred</code> al que le conectamos nuestro
<code>sendComplete()</code> y lo devolvemos.
<code>sendComplete()</code> simplemente borra las líneas
(aparentemente tarde, ya veremos que nos van a hacer falta) y
continúa la cadena de callbacks del deferred; cadena que se va
armando de esta forma: cuando se llama a <code>callback()</code> en
un deferred, éste llama al primer callback. El resultado de este
callback es pasado al siguiente, y así.</p>
<p>Esto así como está manda por un smarthost. La diferencia entre
mandar todo por un smarthost y mandar directamente es que esta
última requiere un paso extra: averiguar a qué máquina debe
entregarse el mail. Me refiero al registro MX. Vamos a tener que
hacer una consulta de DNS mientras recibimos el mail. Una vez que
tengamos ambos vamos a poder hacer la entrega, y finalizar. Veamos
cómo nos las arreglamos:</p>
<pre>
<code>mxCalc= relaymanager.MXCalculator ()
class Relay (object):
implements (smtp.IMessage)
def __init__ (self, router, user):
# deliver by ourselves
self.smarthost= None
resolver= self.getSMTPServer (user)
resolver.addCallback (self.send).addErrback (self.queue)
self.lines= []
self.eom= False
self.router= router
def getSMTPServer (self, user):
return mxCalc.getMX (user.dest.domain)
def lineReceived (self, line):
self.lines.append (line)
def eomReceived (self):
self.eom= True
if self.smarthost is None:
print "WARN: mail received and no smarthost!"
else:
print "mail finished; sending..."
self.send ()
self.sentSignal= defer.Deferred ()
return self.sentSignal
def send (self, mxRecord=None):
if mxRecord is not None:
# mxRecord is a dns.*Record instance
# mxRecord.name is a dns.Name instance
self.smarthost= mxRecord.name.name
if self.eom:
sender= smtp.sendmail (self.smarthost, self.user.orig,
[self.user.dest], '\n'.join (self.lines), config.heloAs)
sender.addCallback (self.sendComplete).addErrback (self.queue)
def queue (self, error):
self.router.queue (error=error, user=self.user, mail=self.lines)
self.sentSignal.callback (True)
def sendComplete (self, *data):
del self.lines
print ignore
self.sentSignal.callback (True)
def connectionLost (self):
print "WARN: unfinished mail!"
del self.lines
self.sentSignal.errback (False)
</code>
</pre>
<p>Acá hay varias cosas. Por un lado tenemos una función que se
encarga de pedir el registro MX, la que devuelve un
<code>Deferred</code> al que le enganchamos nuestra función de
entrega. Al mismo tiempo vamos recibiendo el mail, y cuando termine
también intenta hacer la entrega. Ahora, acá el truco es crear un
<code>Deferred</code> y devolverlo inmediatamente en
<code>eomReceived()</code>. Cuando el mail es enviado finalmente,
nuestro callback <code>sendComplete()</code> es llamado, el que a
su vez hace un callback de nuestro <code>Deferred</code>. Por
último, si tenemos un error de entrega, enconlamos a través de
nuestro router el mail para un posterior intento de entrega.</p>
<p>Lo que vimos en este post es el manejo de
<code>Deferred</code>s, y cómo se los usa para prometer volver a
llamar cuando el resultado está disponible. Hasta ahora es el único
momento en el que realmente he necesitado manejarlos. Supongo que
ya volveré a verlos cuando empieze el duro camino de implementar
filtros.</p>
<p><a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../twismtpy/">twismtpy</a> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../python/">python</a> <span class="selflink">twisted</span></p>
twismtpy-basicohttp://grulicueva.homelinux.net/~mdione/glob//posts/twismtpy-basico/2009-01-22T04:24:04Z2008-07-07T23:28:27Z
<p>Vamos a empezar con un server básico, que es con lo que empecé
yo. El código es prácticamente lo mismo que está en el libro que
mencioné. Básicamnete es un servidor que sabe recibir mails y
guardarlo en maildirs:</p>
<pre>
<code>from twisted.mail import smtp, maildir
from twisted.internet import protocol, reactor
from zope.interface import implements
import os
from email.Header import Header
class MailDir (object):
"""
handles the local delivery to a maildir inbox
"""
implements (smtp.IMessage)
def __init__ (self, user):
userDir= str (user.dest.local)
# we create a directory for this user
if not os.path.exists (userDir):
os.mkdir (userDir)
inboxDir= os.path.join (userDir, 'Inbox')
self.mailbox= maildir.MaildirMailbox (inboxDir)
self.lines= []
def lineReceived (self, line):
self.lines.append (line)
def eomReceived (self):
# message is complete, store it
self.lines.append ('')
messageData= '\n'.join (self.lines)
return self.mailbox.appendMessage (messageData)
def connectionLost (self):
# unexpected loss of connectio, don't save
del (self.lines)
class MailRouter (object):
implements (smtp.IMessageDelivery)
def __init__ (self, validDomains):
self.validDomains= validDomains
def receivedHeader (self, helo, origin, recipients):
# client is how the client ident'ed itself
# clientIP is the ip of the client side's end
# we could do a reverse DNS lookup and check if it's true
# also check on RBL's and such
client, clientIP= helo
recipient= recipients[0]
# this must be our CNAME
myself= 'localhost'
value= """from %s [%s] by %s with ESMTP for %s; %s""" % (
client, clientIP, myself, recipient, smtp.rfc822date ()
)
return "Received: %s" % Header (value)
def validateFrom (self, helo, originAddress):
self.client= helo
# originAddress is a twisted.mail.smtp.Address
# if the from is invalid, we should
# raise smtp.SMTPBadSender
return originAddress
def validateTo (self, user):
"""
routing is the most complicated part of serving an smtp server
we can be run on a laptop that only wants to send mail
with possibly many source address
we can be run on a server that has a local user database;
it con be a smarthost for otehr machines
it can be a satellite machine with only a smarthost
"""
if user.dest.domain in self.validDomains:
return lambda: Maildir (user)
else:
raise smtp.SMTPBadRcpt (user)
class SMTPFactory (protocol.ServerFactory):
def __init__ (self, validDomains):
self.validDomains= validDomains
def buildProtocol (self, addr):
delivery= MailRouter (self.validDomains)
smtpProtocol= smtp.SMTP (delivery)
smtpProtocol.factory= self
return smtpProtocol
if __name__=='__main__':
import sys
# normal local server
domains= sys.argv[1].split (',')
reactor.listenTCP (2525, SMTPFactory (domains))
reactor.run ()
</code>
</pre>
<p>Tenemos tres clases. La primera es <code>SMTPFactory</code>, la
cual es sólo un factory de protocols. Tiene un método,
<code>buildProtocol()</code> que tiene que construir un protocolo y
devolverlo.</p>
<p>La segunda que veremos es la MailDir. Ésta implementa la
interfaz <code>t.m.s.IMessage</code>, que se usa para la entrega de
un mensaje. En este caso es una entrega local a un maildir, aunque
luego implementaremos la entrega remota con esta misma interfaz. La
clase tiene que implementar tres métodos:</p>
<p><code>lineReceived()</code> es llamado por cada nueva línea del
mail que llega. No hace diferencias entre si es parte del cuerpo o
del header. Si tuviéramos que hacer algún procesamiento, como
toquetear headers o rechazar el mail por tamaño, lo deberíamos
hacer en este nivel.</p>
<p><code>eomReceived()</code> se llama cuando todo el mail ha sido
ya entregado a través de <code>lineReceived()</code>. En este caso
escribimos el mail finalmente en un maildir. Fíjense que como
<code>t.m.maildir.MaildirMailbox</code> no tiene esta misma
interfaz, tenemos que acumular la líneas en una lista y pegarlas
todas y dársela de commer a <code>t.m.md.MDMB.append()</code>. Ya
me sentaré a verificar si no hay una mejor API para esto.</p>
<p>Finalmente, <code>connectionLost()</code> se llama si la
conexión se pirde antes de recibir todo el mail.</p>
<p>Hasta ahora todo sencillo, sólo un factory y la implementación
de una entrega local sin muchas luces (no sabe buscar el maildir de
un usuario, sino que asume un directorio propio). Gran parte del
meollo del mail está en la tercera clase que veremos,
<code>MailRouter</code>.</p>
<p>La <code>MailRouter</code> es la que sencargará de definir si el
mail es entregable o no. En nuestro caso inicial, no la picadura
que me estoy rascando, vamos a permitir relaying libre siempre y
sólo localmente a través de la clase <code>MailDir</code>. Antes de
ver lo métodos implementados, veamos una conversación típica en
SMTP:</p>
<pre>
<code>>>> 220 mustang.grulicueva.net NO UCE NO UBE NO RELAY PROBES
<<< helo gurrumin
>>> 250 mustang.grulicueva.net Hello 127.0.0.1, nice to meet you
<<< mail from: mdione@except.com.ar
>>> 250 Sender address accepted
<<< rcpt to: mdione@localhost
>>> 250 Recipient address accepted
<<< rcpt to: root@localhost
>>> 250 Recipient address accepted
<<< data
>>> 354 Continue
<<< Subject: bongs
<<< To: mdione@whitehouse.gov
<<<
<<<
<<< This is top secret info. So secret we won't even tell you. Sorry.
<<< .
>>> 250 Delivery in progress
<<< quit
>>> 221 See you later
</code>
</pre>
<p>Tenemos marcados con <code>>>></code> lo que escupe el
server y con <code><<<</code> lo que manda el cliente.
Básicamente el protocolo se basa en tres fases: presentación,
declaración de entrefa y cuerpo. En la presentación el cliente se
identifica con un nombre. Acá también puede autenticarse con un par
(usuario, passwd), arrancar encripción y todo ese tipo de cosas
relativas a la conexión en si. Luego dice de quién viene y a
quiénes va el mail, y finalmente el mail en sí. Notar que en esta
parte van también los headers (estamos viendo solo dos, un
<code>To</code> y un <code>Subject</code>. Estas dos últimas
estapas se pueden repetir una detrás de la otra las veces que se
quiera.</p>
<p>La clase <code>Mailrouter</code> implementa la interfaz
<code>t.m.s.IMessageDelivery</code>, se crea una por cada conexión
al puerto en el que estamos escuchando, en la que tenemos que
implementar otros tres métodos:</p>
<p>El primero es el <code>validateFrom()</code>, el que es llamado
por cada <code>mail from</code>. Recibe como parametro una tupla de
<code>str</code>. El primer <code>str</code> es el nombre que usó
el cliente en el comando <code>helo</code> y el segundo es el IP
real. También recibe un <code>t.m.s.Address</code> con el from.
Como dicen los comentarios, acá podríamos fijarnos si el nombre y
el ip coinciden, o si está en un RBL o cosas parecidas, o si damos
relay para el from. En este caso aceptamos todo. Notar que lo
devuelto es también un <code>t.m.s.Address</code>. Eventualmente se
podría devolver otro, aunque no se me ocurre en qué casos.</p>
<p>El más importante, el <code>validateTo()</code>. ¿Porqué digo el
más importante? Porque éste es el que se encarga de hacer la
decisión de ruteo, es decir, de decidir qué implementación de
<code>t.m.s.IMessage</code> se va a encargar del delivery basado
tanto en el from como en el to del mail. El parámetro que nos pasan
es un <code>t.m.s.User</code>, el cual contiene ambas direciones en
sus atributos <code>orig</code> y <code>dest</code>
respectivamente. En este caso sólo nos fijamos que el destino esté
entre los dominos al que le hacemos relay local, y si no levantamos
una <code>t.m.s.SMTPBadRcpt</code>, la que se traduce en un mensaje
de no relay al cleinte. Notar que lo que devuelve no es ni una
instancia ni siquiera la clase que va a implementar el delivery,
sino una función que devuelve una instancia. En el próximo post voy
a estar mostrando porqué, y en los subsiguientes posts voy a
complicar este método para lograr las políticas de relaying que
tengo planeado.</p>
<p>Finalmente, <code>receiveHeader()</code> es el más sencillo.
Sólo tenemos que devolver un string con un header Received
apropiado para este delivery. Es éste nos pasan la misma tupla con
dos <code>str</code> que en <code>validateFrom()</code>, la
dirección de origen y la lista de instancias de
<code>t.m.s.User</code> con los recipientes. Ejemplos de estos
headers lo podemos encontrar en cualquier mail:</p>
<pre>
<code>Received: from [192.168.1.77] (unknown [201.250.21.186])
(using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
(No client certificate requested)
by mail.madap.com.ar (Postfix) with ESMTPSA id 978604613
for <mdione@grulic.org.ar>; Mon, 30 Jun 2008 08:16:56 -0300 (ART)
Received: by 10.141.105.17 with HTTP; Wed, 2 Jul 2008 13:55:38 -0700 (PDT)
Received: from localhost ([127.0.0.1] helo=forster.canonical.com)
by forster.canonical.com with esmtp (Exim 4.69 #1 (Debian))
id 1KDHgi-0000eo-Fa
for <mdione@grulic.org.ar>; Mon, 30 Jun 2008 12:36:52 +0100
</code>
</pre>
<p>Bueno, es todo por hoy. Tengan en cuenta que todavía no pretendo
que éste sea un tutorial de Twisted, pero si va mostrando cosas que
nos vamos a encontrar en muchas de las implementaciones de
servicios con este framework.</p>
<p><a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../twismtpy/">twismtpy</a> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../python/">python</a> <span class="selflink">twisted</span></p>
twismtpyhttp://grulicueva.homelinux.net/~mdione/glob//posts/twismtpy/2009-01-22T04:24:04Z2008-07-05T07:02:43Z
<p>Bueno, hoy estoy verbose. Se vé que estoy haciendo cosas de
nuevo...</p>
<p>La cuestión es que, como dije un par de posts atrás, ando con la
idea de hacer un servidor de mail no configurable, sino
programable. Es decir, que en vez de andar toqueteando archivos de
configuración, tratando de adivinar la semántica de cada opción a
partir de los manuales, uno se sienta y lo programa de la forma en
que uno quiera que se comporte. Esta idea surgió en una charla con
otro sysadmin amigo hará unos 3 o 4 años, y quedó dormida hasta que
con Lucio vimos la charla de Lighttpd en San Francisco.</p>
<p>Es obvio que un proyecto de este estilo no funcionaría bien a
menos que la programación sea relativamente sencilla. Y no puede
ser sencilla si el lenguaje no es sencillo. Y qué mejor que python
para esa tarea. Y si hablamos de python, de servidores y apuntamos
un poco alto, no podemos dejar a twisted fuera de la ecuación.</p>
<p>Ahora, si hay algo en twisted es su curva de aprendizaje no
intuitiva. Twisted es un framework para desarrollo de servidores
con un sistema de eventos asíncronos. Con esto se saca un montón de
problemas de escalabilidad asociados a servidores con múltiples
clientes. El tema es que entonces, se tiene que programar el
sistema como una máquina de estados, que en general es así, pero
donde cada estado es prácticamente un procedimiento aparte y además
no se puede quedar haciendo nada pesado. Esto último es porque
twisted es un event loop, y si en uno de los eventos nos quedamos
haciendo cosas sin devolver el control al loop, el loop no puede
procesar otros eventos.</p>
<p>Mas allá de todo eso, me decidí a usarlo lo mismo.
<code>twisted.mail</code> tiene un montón de cosas listas para
usar, sobre todo muchas interfaces, pero la documentación es
inicialmente un poco confusa y no hay un tutorial que uno pueda
seguir. Por suerte en la oficina tenemos un "programming with
Twisted" que justo viene con ejemplos de un server de SMTP y de un
cliente, inclusive explicados.</p>
<p>Comenzando con ése es que me largué a hacer esto. Como la idea
es que sea programable, y con esto lograr la mayor flexibilidad de
configuración, decidí empezar por rascarme donde me pica: me hace
falta un server que sepa usar varios samarthosts, dependiendo de a
qué red esté conectado y de qué cuenta de mail use para enviar
mail, y que soporte encolado cuando no tenga conexión a la red.</p>
<p>En sucesivos posts voy a ir poniendo cachos de código mas o
menos explicando como funciona todo.</p>
<p><a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../python/">python</a> <span class=
"selflink">twisted</span> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../twismtpy/">twismtpy</a></p>
volviendohttp://grulicueva.homelinux.net/~mdione/glob//posts/volviendo/2009-01-22T04:24:04Z2008-07-04T23:29:16Z
<p>¿Qué fueron, 3 meses? Sí, 3, desde que escribí la última vez.
Varias cosas pasaron, desde un período de 2 meses que no tuve
máquina para aflojar un poco la dependencia en ella (por suerte no
hubo lo que podríamos llegar a llamar un desintoxicación) hasta una
pequeña desmotivación para seguir posteando. En el medio sucedieron
cosas como el <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../../posts/pycamp/">PyCamp</a> en Los
Cocos, que empecé a estudiar de nuevo, algunos avances con kreissy,
una reunión de pyar y una casi viajada a Sudáfrica.</p>
<p>Pero todo eso está atrás y he vuelto. Y no sólo al glob, sino de
San Francisco, donde estuvimos el finde con gente de PyAr, en el
marco de las Segundas Jornadas de Software libre que se realizaron
la UTN de allá. la verdad que fue un lindo evento, tal vez no con
el flujo de gente que se esperaba, pero con muy buenas charlas y
mucha joda.</p>
<p>El primer día llegué cerca del mediodía y me apropincué en lo
que luego se convertiría la base de operaciones, la casa de
Perrito. ahí conocí personalmente a humitos, que ya lo conocía del
canal, la lista y de darle hosting para un par de proyectos.
También estaban lucio, alecu, karucha, luciano "yo sólo descubrí el
bug" bello y el "rulo" runa, algunos durmiendo lo que no durmieron
en el bondi de ida, el resto a mate y criollos.</p>
<p>Tipo 15 fuimos para la UTN y llegamos para el acto inaugural.
Después arrancó María Elena Casañas con su charla de soft libre
para principiantes. Afuera en el hall la gente de pyar comenzó a
armar un stand medio improvisdado con las OLPC/XO, stand que tuvo
mucho tráfico de gente. Problemas de wifi y zapatillas aparte,
estuvimos bastante en el stand charlando y mostrando jueguitos en
la XO.</p>
<p>Fui a un par de charlas mas ese día, el Fede Heinz con una
charla de soft libre en la educación y una de OpenSolaris. En esta
última estuvimos discutiendo sobre ZFS con lucio, runa y el pibe
que daba la charla, ezequiel singer. Suena interesante, pues es un
filesystem con jornauling, RAID, LVM y snapshots integrado. Me
traje un cd de OpenSolaris para probar.</p>
<p>A la noche, pizza y cervezas en algún lugar de San Pancho, A la
salida fuiemos con humitos, perrito y ezequiel a tomar una birra a
un pub. Reventados como estábamos, a dormir a las 2.</p>
<p>Al otro día fui a ver otra charla de ezequiel sobre OpenSolaris
(se vé que le están metiendo pata para entrar en la comunidad) y
una de runa sobre lighttpd. El bicho es un server de web
implementado con un core chiquito asincrónico que usa <a href=
"http://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2">sendfile()</a>
para el servido de archivos estáticos y corre cgi's con FastCGI,
SWIG o similares. Se lo vé bastante interesante, sobre todo por que
la configuración es "condicional" y hasta se puede programar un
poco en LUA. A propósito de eso lucio se puso a delirar en hacer
uno en python con [http://twistedmatrix.com/ Twisted] con la
configuración programable, lo que me hizo acordar de mi delirio de
hacer algo así pero para SMTP. Mientras en el lounge darni y alecu
jugaban con una Wii para implementar el juego del sapo.</p>
<p>Después del almuerzo luciano dió una charla de
[http://www.debian.org Debian], y luego y2e con ua de LDAP. Ya
aproximándonos al cierre, alecu hizo un sorteo de cosas que
trajeron de PyCon2008. Yo me gané una <a href=
"http://www.flickr.com/photos/yungyuc/2333547729/">remera</a> con
<a href="http://www.flickr.com/photos/yungyuc/2334374672/">el
chiste</a> de <a href="http://www.xkcd.com">xkcd</a> y a "DJ
Helmet" Casco le regalamos otra como premio consuelo por ser el
primer perdedor y en realidad por haber organizado el evento.</p>
<p>En el evento de cierre hubo más cosas de PyCon2008 repartidas,
merchandising de Sun (pulseritas, muchas, llaveros abrebotellas,
gorras, una remera, dos mochilas) y un libro de MABI y una remera
de delincuente digital de Vía Libre. Luego hubo un surgimiento de
un mercado negro conde hubo intercambio de premios. A todo esto, en
el sorteo se usó el siguiente cacho de código:</p>
<pre>
<code>import random
a= range (1, 48) # había 47 personas
random.shuffle (a)
a.pop (0)
...
</code>
</pre>
<p>Les juro, y hay fotos, aunque no sé dónde (runa?), de que los
primeros dos números fueron el 1 y el 2! Casi se lo comen vivo a
alecu. Luego salieron el 28, 29 y 30. Todos miramos a luciano, pero
él juró que no fue él.</p>
<p>Se hicieron como las 21 y el asado era a las 11 or so, así que
fuimos como para el quincho. Mientras el sobrino de 14 de Casco
hacía el asado ("hago vaquillonas, terneros, lo que sea") estuvimos
delirando un poco más (larguen a un grupo de geeks sin internet y
tiempo libre y algo va a salir). El asado estuvo impecable, y hacia
el final alecu comenzó a delirar sobre un sistema que mantuviera
sincronizado y al día las versiones del soft que usara un grupo de
programadores para desarrollar un proyecto, independiente de qué
versión de la distro se esté usando. Esto nos llevó como una hora,
mientras los locales se caían de sueño, mas el viaje a pata a lo de
perrito (unas 40, 45 cuadras).</p>
<p>En lo de perrito caímos desmayados donde se pudo (quedamos
lucio, humitos, alecu, karucha, perrito, nueces, que había ido el
sábado y yo. Al otro día almorzamos pollo a la parrilla con mucho
chimi, mi graganta ya mostraba signos de invalidez, fuimos a dar
una vuelta por san pancho (domingo a las 4, estaba medio mórtimer),
comer helado y sacar pasajes. Tipo 6 subimos con nueces y lucio al
bondi y vuelvimos.</p>
<p><a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../pyar/">pyar</a> <span class=
"selflink">twisted</span> <a href="http://grulicueva.homelinux.net/~mdione/glob//tags/twisted/../lighttpd/">lighttpd</a></p>