Search code examples
pythontwistedreactortwisted.internet

Twisted unhandled error


When the twisted reactor is running and an exception occurs within a deferred that isn't caught, "Unhandled Error" is printed to the terminal along with a traceback and the exception. Is it possible to handle/intercept these exceptions (e.g., set a callback or override a method)?

EDIT: I'm aware that I can catch a failure by adding an errback to a deferrerd. What I want to know is if there is a way to intercept an unhandled failure/exception that has traversed its way up the chain to the reactor.

EDIT: Essentially, I'm wondering if the twisted reactor has a global error handler or something that can be accessed. I wonder because it prints the traceback and error from the failure.

Example:

Unhandled Error
Traceback (most recent call last):
  File "/var/projects/python/server.py", line 359, in run_server
    return server.run()
  File "/var/projects/python/server.py", line 881, in run
    reactor.run()
  File "/usr/local/lib/python2.6/dist-packages/Twisted-11.0.0-py2.6-linux-x86_64.egg/twisted/internet/base.py", line 1162, in run
    self.mainLoop()
  File "/usr/local/lib/python2.6/dist-packages/Twisted-11.0.0-py2.6-linux-x86_64.egg/twisted/internet/base.py", line 1171, in mainLoop
    self.runUntilCurrent()
--- <exception caught here> ---
  File "/usr/local/lib/python2.6/dist-packages/Twisted-11.0.0-py2.6-linux-x86_64.egg/twisted/internet/base.py", line 793, in runUntilCurrent
    call.func(*call.args, **call.kw)
  File "/var/projects/python/server.py", line 524, in monitor
    elapsed = time.time() - info.last
exceptions.NameError: global name 'info' is not defined

Solution

  • Because these tracebacks are written using a call to twisted.python.log.deferr() (in Twisted 10.2 anyway), it is possible to redirect them using a log observer. This is the most common thing to do with these stack traces. I can't find any base class for log observers (surprisingly) but there are a couple built in:

    twisted.python.log.PythonLoggingObserver - Anything logged goes to the standard Python logging module. (I use this in my application.)

    twisted.python.log.FileLogObserver - Anything logged goes to a file.

    Both of these will catch stack traces reported by the reactor. All you have to do is construct the log observer (no arguments) and then call the object's start() method.

    (Side note: there's also a StdioOnnaStick class that you can construct and assign to sys.stdout or sys.stderr if you want. Then anything you print goes to the Twisted log.)

    To really, truly intercept these calls, so the stack traces never get logged at all, you could either:

    • Subclass twisted.internet.SelectReactor and override its runUntilCurrent() method. That is what logs the stack traces. You would need to study the source of twisted.internet.base.ReactorBase before doing this.
    • After you have done all twisted.* imports, set twisted.python.log.deferr to a function of your choosing, that is compatible with the prototype def err(_stuff=None, _why=None, **kw).