[Twisted-Python] synchronous/asynchronous api: possible interface

Doug Farrell doug.farrell at gmail.com
Fri Nov 13 09:32:25 EST 2009


Hi all,

I'd like to get some comments on the code below (including, "don't be
a bonehead!" <g>). I'm trying to write a API library for database
access that can be used by both synchronous (non-twisted, no reactor)
code and asynchronous (twisted, with reactor) code where the methods
of the library can called in either environment. What I'd like is the
method to return the results of the call (database query results) when
in synchronous mode, and a deferred that is a result of the database
call in asynchronous mode.

What I've done below is create a decorator class called CallerMode
that is used to decorate a function/method that normally returns a
deferred. If the CallerMode class is in synchronous mode it starts a
reactor to get the default callbacks to get called, which gets the
results or exceptions, and stops the reactor.

Am I being a dunce doing this kind of thing? Any suggestions,
comments, etc. are welcome.

Thanks!
Doug


import sys
from functools import wraps
from twisted.internet import reactor
from twisted.python import log
from twisted.web.client import getPage


class CallerMode(object):
    '''This is a decorator class to make calls to an api play nice
    in synchronous or asynchronous mode'''
    ASYNCH = 'asynch'
    SYNCH  = 'synch'
    STATE  = SYNCH

    def __init__(self):
        self.retval = None

    def __call__(self, f):
        '''This provides the decorator function wrapper, which will return
        a deferred for asynchronous mode and the results of the wrapped
        function in synchronous mode'''
        def CB(result):
            self.retval = result
            reactor.stop()
        def EB(failure):
            reactor.stop()
            failure.raiseException()
        @wraps(f)
        def func(*args, **kwargs):
            d = f(*args, **kwargs)
            if CallerMode.STATE == CallerMode.SYNCH:
                d.addCallback(CB).addErrback(EB)
                reactor.run()
            return self.retval if CallerMode.STATE == CallerMode.SYNCH else d
        return func


class Library(object):
    '''This is just a class to provide an API to call for data,
    in this case just using getPage() to get a deferred that gets some
    data, vs. having to set up a database for this test program
    '''
    def __init__(self):
        log.msg('MyLib has been initialized')
        self._retval = None

    @CallerMode()
    def page(self):
        return getPage('http://www.google.com')


def doSomethingWithData(data):
    log.msg(data)


log.FileLogObserver.timeFormat = '%Y-%m-%d %H:%M:%S'
log.startLogging(sys.stdout)

lib = Library()

#CallerMode.STATE = CallerMode.ASYNCH
CallerMode.STATE = CallerMode.SYNCH

# call the library API method
result = lib.page()

# depending on the CallerMode, handle the results differently
if CallerMode.STATE == CallerMode.SYNCH:
    doSomethingWithData(result)
elif CallerMode.STATE == CallerMode.ASYNCH:
    result.addCallback(doSomethingWithData)
    reactor.run()



More information about the Twisted-Python mailing list