Debugging with Manhole

Creating the Manhole Service

In order to create a manhole server, use a command like mktap manhole -u [username] -w [password]. If you've already got a TAP for a server, you can use the argument --append [tapname] to mktap to add a manhole service to that TAP.

Using the Manhole PB Client

The second service offered by twisted.manhole is a Perspective Broker -based server. This gives the client a remote reference to a twisted.manhole.service.Service object, which offers remotely-callable methods to evaluate Python code. With the rich remote-method-invocation facilities provided by PB, however, much more is possible: the client can ask to watch certain objects, and then will receive messages every time that python object is changed. (this takes advantage of some twisted.python code that hooks some functions, like .setattr). These features are described in detail below.

With this in place and running, you're ready to connect with the manhole client. This is a Gtk+-based GUI application named manhole that gets installed along with the rest of twisted. Execute the command manhole to start the client, and it will bring up a dialog that asks for hostname, port number, Service name, username, and password (and also Perspective but don't worry about that for now). Use the default host/port of localhost/8787 to indicate where the twisted.manhole service is listening, and use boss/sekrit for the username and password. Use the default Service name twisted.manhole, and leave the Perspective blank.

Click the Log In button to establish the connection, and you will be greeted with a short message in a window with an output area in the top, and an input area at the bottom. This is just like the python interpreter accessed through the telnet shell, but with a different GUI. You can type arbitrary python code into the input area and get the results in the output area. Note that multi-line sequences are all sent together, so if you define a function (or anything else that uses indentation to tell the interpreter that you aren't finished yet), you'll need to type one additional Return to tell the client to send off the code.

At this point, you can get access to the main Application object just like you did before with the telnet-based shell. You can use that to obtain the Service objects inside it, or references to the Factory objects that are listening on TCP or UDP ports, by doing:

from twisted.internet import app
a = app.theApplication
service = a.getServiceNamed("manhole")
(port, factory, backlog, interface) = a.tcpPorts[0]

After that, you can do anything you want with those objects.

Special Commands

There are a few special commands so far that make debugging Twisted objects really nice. These are /browse and /watch. You can /browse any type of object, and it will give you some nice information about that object in the Spelunking window that pops up when manhole establishes a connection to the manhole Service. /watch-ing an object adds hooks to the object, allowing you to watch modifications to it in real time. Try the following in the manhole window and watch what happens in the Spelunking box (word wrapped for clarity):

/browse ["hello", "there"]
 <ObjectLink of ["hello", "there"] type list>:
  ['hello',
   'there',]


class A:
    def foo(self):
        self.x = 1

x = A()
/browse x
<ObjectLink of x type instance>:
  {members: {}
   class: 'A'
   methods: {}}


/watch x
  <ObjectLink of x type instance>:
  {members: {}
   class: 'A'
   methods: {}}


x.foo()

<ObjectLink of x type instance>:
  {members: {x: 1}
   class: 'twisted.python.explorer.WatchingA8195574'
   methods:
     {foo:
        <ObjectLink of x.foo type instance_method>:
          {class: 'twisted.python.explorer.WatchingA8195574'
           self: '<twisted.python.explorer.WatchingA8195574 instance at
                 0x8195574>'
           doc:
             Pretend to be the method I replaced, and ring the bell.
                     
           line: 651
           signature:
             [{name: instance},
              {name: a
               list: 1},
              {name: kw
               keywords: 1},]
           file: /home/punck/cvs/Twisted/twisted/python/explorer.py
           name: __call__}
        }}

<ObjectLink of x type instance>:
  {members: {x: 1}
   class: 'twisted.python.explorer.WatchingA8195574'
   methods:
     {foo:
        <ObjectLink of x.foo type instance_method>:
          {class: 'twisted.python.explorer.WatchingA8195574'
           self: '<twisted.python.explorer.WatchingA8195574 instance at
                 0x8195574>'
           doc:
             Pretend to be the method I replaced, and ring the bell.
                     
           line: 651
           signature:
             [{name: instance},
              {name: a
               list: 1},
              {name: kw
               keywords: 1},]
           file: /home/punck/cvs/Twisted/twisted/python/explorer.py
           name: __call__}
        }}

   

TODO: /watch might be broken right now.

As you can see, /watch really gives you a lot of power (and a lot of output, too -- hopefully we'll have a nice GUI display for this in the future). The /browse and /watch functionality is brought to you by the twisted.manhole.explorer module, which was written largely by Kevin Turner.

TODO: Add an example using twisted.python.rebuild.rebuild. This lets you tell your application (remotely) to reload its classes, allowing you to upgrade a running server without missing a beat.

Have fun!