Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

The Jpydbg Debugger


August, 2004: The Jpydbg Debugger

A debugger plug-in for Python

Jean-Yves is CTO of SEFAS, a French software company where he works on development tools and computer languages. He can be reached at jymengantteaser.fr.


The architectures of open-development environments such as Eclipse and JEdit are based on the concept of frameworks with plug-in components. While a number of Java debugger plug-ins have been released for these IDEs, there are only a few for Python. Consequently, I present in this article Jpydbg, a Python debugging plug-in for JEdit (http://www.jedit.org/). Jpydbg provides a graphical interface and shell for debugging Python programs. The debugger's backend is implemented as a networking debugger, inheriting the standard dbd.py Python debugging kernel, while the graphical frontend is based on the Swing GUI interface. The complete source code and related files for Jpydbg are available electronically (see "Resource Center," page 5) and at http://jpydbg.sourceforge.net/.

Python Debugging Basics

Of course, you may wonder why I just didn't use Python's basic pdb debugger instead of writing a new one. The reason is that pdb is character based (based on stdin/stdout), which makes it impractical (if not impossible) to integrate into a GUI-based IDE like JEdit. That said, both pdb and Jpydbg inherit from the bdb.py class, which is central to any Python debugging tool. Moreover, the bdb.py class makes it possible for debuggers to acquire necessary information about the program being debugged. Central to this is tracefunc, a callback function (part of the C Python kernel), which serves as a default entry point for debuggers; see Example 1. The important parameters of tracefunc are:

  • self, the current bdb instance.
  • frame, the current running Python frame instance.
  • event, the debugging event type at the origin of current callback.
  • arg, which represents complementary information depending on event populated type.

The Jpydbg child class methods dispatch_line, dispatch_call, dispatch_return, and dispatch_exception take precedence over the bdb methods called by the bdb dispatcher. Once the Python interpreter returns control, you can begin collecting interesting debugging information—source file location of the running script, current source line in progress, global variables, local variables, and the like. Of particular interest is the Python frame structure, one of the parameters of the callback function (Example 1). The frame structure gives you the current Python execution context in progress. For details on the frame structure, refer to frameobject.h and frameobject.c (part of the Python kernel).

Finally, Jpydbg uses the standard sys.settrace Python function to activate/deactivate the callback when needed. To activate the callback, Jpydbg uses sys.settrace(trace_dispatch), the candidate method object to activate; to deactivate, it uses None.

Jpydbg Design

Again, Jpydbg is a client/server, TCP/IP daemon-based debugger. For maximum flexibility, at startup Jpydbg either listens on a dedicated port waiting for incoming debug frontend solicitors, or connects back to a listening remote debugger frontend when hostname and ports are provided as command-line arguments. The latter strategy is better for remote debugging, and works even if there's a firewall between the debugger and debuggee. Of course, I could have based Jpydbg on nonTCP/IP protocols such as DLLs. However, an advantage to the TCP/IP-based approach is the clean process isolation between the debugger and debuggee. Among other things, this clean isolation gives you the capability of writing the debugger GUI frontend in a different language or platform. It also reinforces compliance with Heisenberg's principle, which refers to how complex it is to measure and observe a system (the debuggee) without the observation (the debugger) disturbing the system and changing its behavior.

As Figure 1 illustrates, on the client side the Jpydbg frontend (which is written in Java) sends text-based, command-line requests containing optional arguments. On the server side, the backend debugger (written in Python) parses the received requests and sends the results and the debugger's events back in XML format over the TCP/IP session. As you can see in Figure 2, Jpydbg's XML DTD is straightforward.

Likewise, the jpydaemon.py structure (available electronically) is also straightforward. The initial part consists of setting up a TCP/IP-based socket protocol and entering a message network wait loop, which waits for commands to come in. There are two kinds of initial commands that may come in:

  • A Python shell command, which is executed like a standard character-mode Python shell. The only difference is that the command stdout is sent back in XML format over the wire.
  • A debugging startup request command, which is included with the Python module and optional program arguments. This command makes jpydaemon.py enter debugging mode, which then launches the debuggee through the inherited bdb.py run method. Debugging callbacks are set by the run method before activating the debuggee.

Once a debugging event (say, a new Python line entered or breakpoint reached) is triggered, the Python kernel gives control to one of the overridden jpydaemon.py debugging event-listener methods—user_call, user_return, user_line, or user_exception. Globally, there are only a couple of difficulties you face during implementation: One is taking care of mangling XML reserved words and character tokens inside a debugging message; another is capturing/dispatching exceptions, because exceptions such as SystemExit, SyntaxError, NameError, or ImportError need to be captured and handled on the backend side to generate specific debug events.

Actually, it is a bit of a misnomer to say at this point that the Jpydbg GUI frontend is a "Java frontend." Before calling it that, you need to integrate the debugger and IDE, such as Eclipse (using SWT) or JEdit (using JFC/Swing). The ClientDebuggerShell.java class (available electronically) is included for debugging XML debugging-generated messages via the quick and dirty Debug Rough Command Interface; see Figure 3. Note that this utility class is not usable as a Python debugger in itself, and only debugs XML backend information.

The JEdit Plug-In

Jedit is a freely available GNU cross-platform IDE/editor that provides an extension plug-in API. JEdit-supported components can be downloaded and automatically integrated into JEdit without leaving the IDE.

The way plug-ins are integrated within JEdit is a model of simplicity and robustness. In general, GUI components for JEdit are built using the standard Java Swing interface and bundled into a separate package—org.jymc.jpydebug.swing.ui. This package contains the main debugger's Frame container and associated tab panels, which represents the foundations of the debugger's interface. This package also represents a reasonable isolation for the GUI layer from the JEdit IDE plug-in semantics.

A second package of classes manages the JEdit plug-in interaction with the debugging layer. Figure 4 shows the Jpydbg frontend and the XML jpydbg.py layer actively debugging the Python Zope kernel. In addition to the debugging layer, the full plug-in contains a Python ClassBrowsing tree and Python Syntax checker.

Conclusion

Relying on and inheriting the bdb.py kernel debug library makes it possible to have reliable software and new tools like Jpydbg. Moreover, the Python jpydbg.py module can be integrated inside the standard Python library module to provide a full network-based XML library fully usable to implement independent robust GUI client-side debugging environments.

DDJ


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.