Runs the Twisted reactor event loop in a thread alongside an IPython shell, for introspecting a running Twisted process.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | """ipy.py"""
try:
from IPython.Shell import MTInteractiveShell
from IPython.ipmaker import make_IPython
import threading
def hijack_reactor():
"""Modifies Twisted's reactor with a dummy so user code does
not block IPython. This function returns the original
'twisted.internet.reactor' that has been hijacked.
NOTE: Make sure you call this *AFTER* you've installed
the reactor of your choice.
"""
from twisted import internet
orig_reactor = internet.reactor
class DummyReactor(object):
def run(self):
pass
def __getattr__(self, name):
return getattr(orig_reactor, name)
def __setattr__(self, name, value):
return setattr(orig_reactor, name, value)
internet.reactor = DummyReactor()
return orig_reactor
class IPShellTwisted(threading.Thread):
"""Run a Twisted reactor while in an IPython session.
Python commands can be passed to the thread where they will be
executed. This is implemented by periodically checking for
passed code using a Twisted reactor callback.
"""
TIMEOUT = 0.01 # Millisecond interval between reactor runs.
def __init__(self, argv=None, user_ns=None, debug=1,
shell_class=MTInteractiveShell):
from twisted.internet import reactor
self.reactor = hijack_reactor()
mainquit = self.reactor.stop
# Make sure IPython keeps going after reactor stop.
def reactorstop():
pass
self.reactor.stop = reactorstop
reactorrun_orig = self.reactor.run
self.quitting = False
def reactorrun():
while True and not self.quitting:
reactorrun_orig()
self.reactor.run = reactorrun
self.IP = make_IPython(argv, user_ns=user_ns, debug=debug,
shell_class=shell_class,
on_kill=[mainquit])
threading.Thread.__init__(self)
def run(self):
self.IP.mainloop()
self.quitting = True
self.IP.kill()
def mainloop(self):
self.reactor.callLater(self.TIMEOUT, self.on_timer)
self.start()
self.reactor.run()
self.join()
def on_timer(self):
self.IP.runcode()
self.reactor.callLater(self.TIMEOUT, self.on_timer)
exists = True
except ImportError:
exists = False
if __name__ == '__main__':
# Sample usage.
# Create the shell object. This steals twisted.internet.reactor
# for its own purposes, to make sure you've already installed a
# reactor of your choice.
shell = IPShellTwisted(
argv=[],
user_ns={'__name__': '__example__',
'hello': 'world',
},
)
# Run the mainloop. This runs the actual reactor.run() method.
# The twisted.internet.reactor object at this point is a dummy
# object that passes through to the actual reactor, but prevents
# run() from being called on it again.
shell.mainloop()
# You must exit IPython to terminate your program.
print 'Goodbye!'
|
By using the same cleverness that IPython uses to run the GTK and WX mainloops in a thread alongside IPython, here is a module that can be used to run the Twisted reactor event loop in a separate thread alongside an IPython shell on standard i/o.
By populating the IPython namespace, you can introspect and control a running process quite handily.
Seems to work well enough on both Linux and Windows under Python 2.3 and 2.4.
Requires that IPython is installed. Get it from http://ipython.scipy.org/
Improvements welcome!
Originally written for use with Schevo ( http://schevo.org/ ).
Also check out Bob's solution. http://bob.pythonmac.org/archives/2005/04/17/twisted-and-foreign-event-loops/
great info. Thanks for the link.
I'm still using Twisted 1.3.0 at the moment so I'll need to continue to use the recipe as-is.
When I migrate to Twisted 2.x I'll be sure to post a new recipe that uses the approach that Bob advocates.