Cogen and greenlets

19 January 2009 (updated 04 March 2015)

I was playing these days with greenlets and I finally managed to find some time to make and coroutine implementation for cogen that uses the seamless stack switching of greenlets instead of the usual generator yield-based switching.

The best part about this is that cogen could support any sort of old code that uses the usual file or socket apis in the standard python library.

Unfortunately greenlets don't work with python 2.6 right now; let me shamelessly quote this very nice reply I got from one of py.lib's (greenlets are part of py.lib) maintainers:

"I mean come on - it's open source, if it works for me on 2.5 why I would bother too much?"

For the love of god, is this the right attitude to run a project these days?! I mean come on ...

Anyhow, nevermind that.

So back to those coroutines using greenlets - I named them corolets - feels like a good namegiven the fact the the regular generator stuff is named with coroutine and there's some need for distinction. I also added some socket wrappers in a module socketlets (not very inspired). It has a Socket class that provides regular blocking interface and makes use of the good old _fileobject class from the socket module in stdlib for the makefile stuff.

I got those 2 pieces in cogen.magic.corolets and cogen.magic.socketlets. For those who didn't understood much here's an example:

import sys

from cogen.core import schedulers
from cogen.magic.corolets import corolet, yield_
from cogen.magic import socketlets

@corolet
def server():
    srv = socketlets.Socket()
    adr = ('0.0.0.0', len(sys.argv)>1 and int(sys.argv[1]) or 1200)
    srv.bind(adr)
    srv.listen(64)
    while 1:
        print "Listening on", adr
        conn, addr = srv.accept()
        print "Connection from %s:%s" % addr
        m.add(handler, args=(conn, addr))

@corolet
def handler(sock, addr):
    fh = sock.makefile()
    fh.write("WELCOME TO ECHO SERVER!\r\n")
    fh.flush()

    while 1:
        line = fh.readline(1024)
        if line.strip() == 'exit':
            fh.write("GOOD BYE")
            fh.close()
            sock.close()
            return
        fh.write(line)
        fh.flush()

m = schedulers.Scheduler()
m.add(server)
m.run()

Compare that to the regular generator based example (don't miss those yields):

import sys

from cogen.core import sockets
from cogen.core import schedulers
from cogen.core.coroutines import coroutine

@coroutine
def server():
    srv = sockets.Socket()
    adr = ('0.0.0.0', len(sys.argv)>1 and int(sys.argv[1]) or 1200)
    srv.bind(adr)
    srv.listen(64)
    while 1:
        print "Listening on", adr
        conn, addr = yield srv.accept()
        print "Connection from %s:%s" % addr
        m.add(handler, args=(conn, addr))

@coroutine
def handler(sock, addr):
    fh = sock.makefile()
    yield fh.write("WELCOME TO ECHO SERVER!\r\n")
    yield fh.flush()

    while 1:
        line = yield fh.readline(1024)
        if line.strip() == 'exit':
            yield fh.write("GOOD BYE")
            yield fh.close()
            sock.close()
            return
        yield fh.write(line)
        yield fh.flush()

m = schedulers.Scheduler()
m.add(server)
m.run()

It's pretty rough right now - I still need to add tests and check the performance, but I wanted to give an idea of what's going on for the braver ones :)

This entry was tagged as cogen greenlets python