[Twisted-Python] modal wxPython, Twisted, xml-rpc; example code

Andrew Dalke dalke at dalkescientific.com
Tue Jun 3 19:43:57 MDT 2003


Me:
> I've attached some example code which shows how to use
> a modal wxPython dialog and Twisted to produce a responsive
> GUI which uses XML-RPC to query a remote XML-RPC service to
> get a US state name given it's position in a sorted list.
>
> I've also contributed this as an ActiveState recipe.

However, it fails for some cases of a modal in a modal.

I have a set of error cases in the code, which looks like this (except
the last one, which I added today).

         if TEST_ERRORS:
             i = self.i
             if i == 3:
                 proxy = Proxy("http://illegal-host_name/")
             elif i == 6:
                 proxy = Proxy("http://beatty.userland.com/")
             elif i == 8:
                 proxy = 
Proxy("http://beatty.userland.com/testing_xmlrpc_error_case")
             elif i == 9:
                 proxy = Proxy("http://beatty.userland.com:9876/")

The last one cases an error during making the connection, and so I get
a "unable to connect to remote host" message.  Now, I want to show that 
message
in another modal, which doesn't use Twisted, so I just opened the modal 
window.

This starts up the wxPython event loop again, which triggers another 
timer
event, which calls back to the Twisted reactor to run another iteration.

It appears that my defered is still active, because I then get an 
'AlreadyCalledError'.
Here's the full stack trace.


Traceback (most recent call last):
   File "Progress.py", line 158, in Bad
     try_again = task.bad(fail)
   File "Progress.py", line 52, in bad
     wxCANCEL | wxYES_NO | wxICON_QUESTION)
   File "Progress.py", line 208, in OnTimer
     reactor.doIteration(0)
   File "E:\Python22\Lib\site-packages\twisted\internet\default.py", 
line 473, in
  doSelect
     _logrun(selectable, _drdw, selectable, method, dict)
--- <exception caught here> ---
   File "E:\Python22\Lib\site-packages\twisted\python\log.py", line 64, 
in callWithLogger
     callWithContext({"system": lp}, func, *args, **kw)
   File "E:\Python22\Lib\site-packages\twisted\python\log.py", line 51, 
in callWithContext
     return context.call({ILogContext: newCtx}, func, *args, **kw)
   File "E:\Python22\Lib\site-packages\twisted\python\context.py", line 
32, in callWithContext
     return func(*args,**kw)
   File "E:\Python22\Lib\site-packages\twisted\internet\default.py", 
line 498, in _doReadOrWrite
     selectable.connectionLost(failure.Failure(why))
   File "E:\Python22\Lib\site-packages\twisted\internet\tcp.py", line 
396, in connectionLost
     self.failIfNotConnected(error.ConnectError())
   File "E:\Python22\Lib\site-packages\twisted\internet\tcp.py", line 
315, in failIfNotConnected
     self.connector.connectionFailed(failure.Failure(err))
   File "E:\Python22\Lib\site-packages\twisted\internet\base.py", line 
527, in connectionFailed
     self.factory.clientConnectionFailed(self, reason)
   File "E:\Python22\Lib\site-packages\twisted\web\xmlrpc.py", line 209, 
in clientConnectionLost
     self.deferred.errback(reason)
   File "E:\Python22\lib\site-packages\twisted\internet\defer.py", line 
215, in errback
     self._startRunCallbacks(fail, 1)
   File "E:\Python22\lib\site-packages\twisted\internet\defer.py", line 
244, in _startRunCallbacks
     raise AlreadyCalledError()
twisted.internet.defer.AlreadyCalledError:


I can't figure out what's going on.  The code I could find for the 
reactor is very
careful to take defereds off the pending list before calling, so 
there's no way to
call them twice.

There's a clue in that only the last of these trigger this error 
condition.  This
suggests something with how the connection is established, and not 
after it's
put into place.  What I think is happening is that that
twisted.internet.base.BaseConnector allows the factory to reattempt the 
connection
when the intial connection failed, and there isn't a guard in case the 
connectionFailed
gets called again.

I still haven't grokked how the deep part of Twisted works - tried for 
the last
while to track this bug further - so I'll leave it up to you all.

In the meanwhile, since I don't need Twisted in the new (sub)modal, my 
workaround
is to do

     def OnTimer(self, event):
         if self.recursive:
             return
         self.recursive = 1
         try:
             reactor.runUntilCurrent()
             reactor.doIteration(0)
         finally:
             self.recursive = 0

					Andrew
					dalke at dalkescientific.com





More information about the Twisted-Python mailing list