## Debugger

Boa now has a stable out of process debugger that can debug almost any Python application. This includes threads, most popular Python GUI toolkits, Zope Python debugging and even specialised support for debugging Zope Python Scripts.

To debug an application, save any unsaved changes and click the red play button on the toolbar or choose File->Debug application. This applies to application modules and any modules part of an application. To debug an individual module that is part of an application, choose File->Debug module.
The Debugger window should appear at the same place as the Inspector and debugging will trace until the very first line of source.

Important: When debugging wxPython applications on wxPython versions lower than 2.3.3 on windows, there is a bug in wxProcess/wxExecute which prevents the main frame of the application from being displayed.
To work around this the main frame must be shown, hidden and shown again. e.g.

    frame.Show();frame.Hide();frame.Show()

The standard debugging pages:

Stack | List of items, each representing a level of the execution stack. The selected item represents the current frame for the watch and namespace windows. Double-click on an item to jump to the corresponding line in the source.
Breakpoints | List of breakpoints defined for the debugger. Breakpoints can be edited, disabled or deleted by selecting them and right clicking in this view. Double-clicking on a breakpoint will take you to where it is defined in the source. Add a breakpoint by clicking on the left side of the target line in the code next to the line number or by putting your caret at the desired line in the source and choosing Edit->Toggle breakpoint. Hard breakpoints are breakpoints defined in your source code like this:
    if hasattr(sys, 'breakpoint'): sys.breakpoint()
When the debugger encounters a hard breakpoint it will be added to the breakpoint list. You may also mark a hard breakpoint with a soft one before it is triggered by the debugger. This allows you disable hardpoints. Right-click on a breakpoint in the breakpoint list for options. Hard breakpoints are primarily useful for debugging threads. They can also be used to speed up startup time of an application being debugged. There is an inevitable slowdown because of the overhead of tracing the executing code. When the Debug / Continue full speed button is clicked, the execution continues without the overhead of the tracer. The only way to break into the execution now is with a hard breakpoint.
Watches | Shows a list of user defined watches, evaluated in the scope of the selected stack level. Select/right-click for menu. New watches can be added from the Locals and Globals pages or from the right-click menu of the Watches page. Double-click an existing watch to edit it. Note that watches does not need to be only variable names, any valid Python expression is allowed. Another way to watch a variable is to put the Debugger in Debug Browsing mode. On the Toolbar, toggle the button. Like Code Browsing you have to hold down the Ctrl key and move your mouse over variables in the source code. The name in the source should be underlined in red and it's current value is displayed in the Editor status bar. Clicking on an underlined variable adds it as a watch. The last way to inspect variables is to use the Shell Debug mode. On the Toolbar, toggle the button. The Editor should switch to the Shell and display a debugging prompt. Here you may also type any valid Python expression. Toggle the button again to return to a normal shell prompt.
Locals | List of all objects in local scope of the selected stack level. Select/right-click to add as a watch or add your own locals watch Note that this view is only updated when it is active, when there are many objects in scope this can degrade performance so only keep it active when you need it.
Globals | List of all objects in global scope of the selected stack level. Select/right-click to add as a watch or add your own globals watch. Note that this view is only updated when it is active, when there are many objects in scope this can degrade performance so only keep it active when you need it.

### Debugging Threads

The biggest difficulty in debugging Python threads is that the Python debug tracing hook must be set per thread. Therefor a normal breakpoint in a other thread than the main thread (the one the debugger started up with) will never be hit by the main thread's tracer.
Hard breakpoints to the rescue! When a hard breakpoint is triggered from within a non-main-thread the debugger has access to the thread and can set the tracer for it, allowing us to step further.

### Remote Debugging

Because the debugger server runs out of process anyway (controlled via xmlrpc) it is a small step to debugging a process on a different machine.

The first step is to start up the process that you want to debug on the remote machine, create a RemoteServer and install the tracer.

There are a few ways to do this. One way is to create a script called e.g. bcrdb which looks like this:

    #!/usr/bin/python

    import sys
    sys.path.append('/path/to/boa')

    from Debugger.RemoteServer import start
    start('username', 'password')

    del sys.argv[0:1]
    execfile(sys.argv[0], {'__name__': '__main__',
                           '__builtins__': __builtins__})

You can now use this script (bcrdb script) instead of Python (python script) to debug remote scripts.

You have to add the following snippet to your program to turn on the tracer:

    if hasattr(sys, 'debugger_control'):
        sys.debugger_control.set_traceable()

Breakpoints encountered in code after the tracer was installed will be caught by the Debug Server.

If Boa is installed normally on the remote machine, Boa.py -R script can be used instead of bcrdb script. (Boa.py imports almost nothing on this code path so no 'pollution' to worry about)

The second step is to connect to the Remote Debugger from the Boa IDE. From an open python module, select File->Attach to debugger, and fill in the dialog. You should now be attached to the remote debugger.

It is your responsibility to assure that the file paths to the source files are identical on both machines.

### Zope Debugging

Two forms of Zope debugging is provided.

Firstly you may debug Zope as a normal Python application.
Open up z2.py in the Editor, set it's command-line parameter to -D (File->Set command-line parameters). Now debug it like any other module.
Set a hard breakpoint in your Product code to break into your code.

Secondly you may directly debug and step through Zope Python Scripts. You should have a working Zope Connection defined in the Explorer. Open the Python Script you want to debug.
Now run Zope as described in the first step. The easiest way to break into a Python Script (remember that they execute in a different thread than z2.py) is to install the extremely small Zope Product found in Debugger/ZopeBreakpoint.zip. All this product does is to allow Scripts to access sys.breakpoint. Then using the normal sys.breakpoint() call in your Python Script will break at that line. From now on, normal soft breakpoints will work for the Script.
If you don't want to install the Product you can still step through Python Scripts by setting a hard break somewhere in the Zope code that runs in the same thread as the Script. After hitting the hard break, normal breakpoints in your Script should work.

Because of the new Signals support added to Zope 2.6. under unix platforms z2.py must be patched on those platforms.
(Because the debugger doesn't run programs in the main thread and Signals require this).

To patch z2.py, from line 526, change:

    # install signal handlers if on posix
    if os.name == 'posix':
        from Signals import Signals
        Signals.registerZopeSignals()

to

    # install signal handlers if on posix
    if os.name == 'posix' and not hasattr(sys, 'boa_debugger'):
        from Signals import Signals
        Signals.registerZopeSignals()

There is limited support provided for debugging Page Templates too.
You may add soft breakpoints next to <tal:*> tags inside <metal:use-macro*> sections. The debugger should stop at these breakpoints and you can inspect the namespace with watches or the Debugger shell. Stepping is not supported.

Another way to debug Zope from Boa is to install the functions in the Debugger/BoaDebugServer.py module as ExternalMethods.

This allows you to start and hook Zope into the Boa Debug Server at runtime without running z2.py in the debugger from the IDE.
(This also avoids the Signal problems problem on linux and works with an unpatched z2.py)

To install the runtime hooking follow the next steps:

- Boa must be installed on the Zope machine.
- Copy Debugger/BoaDebugServer.py to Zope's Extensions directory.
- Edit BoaDebugServer.py and replace c:/Path/To/Boa with the actual path to the installation. You may also optionally replace the username, password passed to the start function for security.
- You may create the ExternalMethods anywhere you please, I have the following structure:
  BoaDebugger [Folder]
  StartDebugServer [ExternalMethod]
  HookDebugServer [ExternalMethod]

To use the runtime hooking follow these steps:

- With a browser, execute the StartDebugServer method.
- From the IDE (with any Python module open) choose File->Attach to debugger.
- With a browser, execute the HookDebugServer method.

The debugger is now hooked and ready and soft breakpoints should work.

Boa Constructor - Application Help - Debugger