Welcome, guest | Sign In | My Account | Store | Cart

Runs the Twisted reactor event loop in a thread alongside an IPython shell, for introspecting a running Twisted process.

Python, 111 lines
  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/ ).

2 comments

amir 18 years, 11 months ago  # | flag
Matthew Scott (author) 18 years, 11 months ago  # | flag

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.