The @string debugger_kind setting controls what debugger the 'Debug Script' script button uses. Eventually this setting will control what debugger the debug command uses. At present the only valid values are 'winpdb' and 'idle'. Support for idle isn't complete yet: I have some questions (see below) out about idle that must be answered.

I figured out how to have the 'Debug Script' button debug scripts with c, g and p predefined.

First, the user must first run Leo from the debugger: I use this batch file to run test.leo with winpdb or idle:

cd c:\prog\tigris-cvs\leo\test

REM Use idle
REM c:\python24\python.exe c:\python24\Lib\idlelib\idle.py -d -r
c:\python24\Scripts\runLeoTest.py

REM Use winpdb (do NOT use -c option)
c:\python24\python.exe c:\python24\Scripts\_winpdb.py -t ..\src\leo.py test.leo

As you can see, when using idle a helper file, runLeoTest.py is required. It is:

import os, import sys
sys.path.append(r'c:\prog\tigris-cvs\leo\src')
import leo
leo.run( os.path.join(src,'..','test','test.leo'))

After starting the debugger, you simply use the debuggers 'Run' command to run Leo. Now that Leo is running in the debugger, it is possible to drop back into the debugger to single-step through scripts. This requires a hack. The 'Debug Script' button creates a file called leoScriptModule.py as a way of gaining access to c, g and p. Suppose the script is:

print 'hi'
print c

The 'Debug Script' button sets g.app.scriptDict['c']? = c and writes the following to leoScriptModule.py:

# A module holding the script to be debugged.
import rpdb2
if rpdb2.g_debugger is not None: # don't hang if the debugger isn't running.
  rpdb2.start_embedded_debugger(pwd="",fAllowUnencrypted=True) # Hard breakpoint.
# Predefine c, g and p.
import leoGlobals as g
c = g.app.scriptDict.get("c")
p = c.currentPosition()
# Actual script starts here.
print 'hi'
print c

The 'Debug Script' button then forces a clean import of leoScriptModule as follows:

if 'leoScriptModule' in sys.modules.keys():
    del sys.modules ['leoScriptModule']
import leoScriptModule

As a result of this import, Python executes the top-level code in leoScriptModule.py. Assuming the winpdb debugger is running, the line:

rpdb2.start_embedded_debugger(pwd="",fAllowUnencrypted=True)

drops into winpdb, much like pdb.set_trace() does.

You, the user, can now single step through this code! The lines:

import leoGlobals as g
c = g.app.scriptDict.get("c")
p = c.currentPosition()

set up c, g and p properly, and you can now single step through your script. Single stepping to the end, or hitting the debuggers 'Run' button will return you to Leo's event loop. BTW, the rest of Leo hangs while single-stepping through the script.

All this may sound complicated, but in practice debugging scripts is easy:

That's all. A few problems remain. In order to support a new debugger, Leo needs to know:

Question A: How to tell whether the debugger is, in fact running and

Question B: How to simulate a 'hard' breakpoint.

The code shown above in leoScriptModule.py works only for winpdb. I haven't figured out how to do this for Idle.

The leoScriptModule hack has exposed a bug in winpdb. winpdb doesn't update the source listing when leoScriptModule.py changes. So if you change the script being debugged, winpdb will show you the old version of the script. It's a strange experience: the debugger will execute the correct code but will show you the incorrect code.

Conclusion

The recent Aha to run debuggers from a totally separate process from Leo continues to simplify all aspects of Leo's debugger project: