[Twisted-Python] adbapi weirdness

Andrew Bennetts andrew-twisted at puzzling.org
Thu Jul 31 01:08:49 EDT 2003


On Wed, Jul 30, 2003 at 09:30:27PM -0700, Steve Freitas wrote:
> > > Hi all,
> > > 
> > > Here's the latest thing I can't figure out.
> > > 
> > > If I do this and make an XML-RPC call to dbtest(), it runs fine:
> > > 
> > > class takeOne(xmlrpc.XMLRPC):
> > >     def __init__(self):
> > >         self.dbpool = adbapi.ConnectionPool("pyPgSQL.PgSQL", \
> > >           host='127.0.0.1', user='postgres', database='template1')
> > >         self.db = adbapi.Augmentation(self.dbpool)
> > >         self.prefs = Env(self.db)
[...]
> > > 
> > >     def xmlrpc_listPrefs(self):
> > >         return self.prefs.prefs
> > > 
> > > class Env:
> > >     def __init__(self, db):
> > >         self.db = db
> > >         self.prefs = self.db.runQuery("select * from prefs")
[...]
> > > But a call to listPrefs() hangs until I make another, concurrent
> > > request to dbtest(), at which point it comes through. Then if I try
> > > listPrefs() again, it returns Fault 8002: Can't serialize output.
> > 
> > You can't send the same Deferred more than once.
> 
> Err, can you give it to me in NewbieSpeaque(tm)? :-)

I'll try :)

runQuery returns a Deferred.  If you don't know what that is, you're going
to run into bigger problems than just this, so speak up now :) -- and read:
    twistedmatrix.com/documents/howto/defer

Now, for twisted.web.xmlrpc to get the results of your deferred, it has to
add a callback to it.  This callback consumes the result of your deferred
(although I don't see any reason why this *has* to be the case, necessarily,
but I can see how it could avoid confusing errors, so it's probably a good
idea).  This means that the next callback added to that deferred will get a
result of None, which can't be represented in XML-RPC, hence the error.

One way to work around this would be something like this (completely
untested, of course...):

----
from twisted.web import xmlrpc
from twisted.internet import defer
from twisted.python import log
from twisted.enterprise import adbapi

class TakeOne(xmlrpc.XMLRPC):
    def __init__(self):
        self.dbpool = adbapi.ConnectionPool("pyPgSQL.PgSQL", \
                host='127.0.0.1', user='postgres', database='template1')
        self.db = adbapi.Augmentation(self.dbpool)
        self.prefs = Env(self.db)

    def xmlrpc_listPrefs(self):
        return self.prefs.getPrefs()

class Env:
    def __init__(self, db):
        self.db = db
        self.prefs = None
        self.outstandingRequests = []
        self.d = self.db.runQuery("select * from prefs")
        d.addCallbacks(self._cb, self._eb)

    def getPrefs(self):
        if self.prefs is not None:
            return self.prefs
        else:
            d = defer.Deferred()
            self.outstandingRequests.append(d)
            return d

    def _cb(self, result):
        self.prefs = results
        for d in self.outstandingRequests:
            d.callback(result)
        del self.outstandingRequests[:]

    def _eb(self, failure):
        self.prefs = results
        log.err(failure)
----

-Andrew.





More information about the Twisted-Python mailing list