diff --git a/Include/eval.h b/Include/eval.h index 66638e75d13..b78dfe0fae0 100644 --- a/Include/eval.h +++ b/Include/eval.h @@ -17,6 +17,8 @@ PyAPI_FUNC(PyObject *) PyEval_EvalCodeEx(PyCodeObject *co, PyObject **defs, int defc, PyObject *closure); +PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args); + #ifdef __cplusplus } #endif diff --git a/Lib/pdb.py b/Lib/pdb.py index d7215cfa7b7..46dff55c915 100755 --- a/Lib/pdb.py +++ b/Lib/pdb.py @@ -523,6 +523,33 @@ class Pdb(bdb.Bdb, cmd.Cmd): print '*** Jump failed:', e do_j = do_jump + def do_debug(self, arg): + sys.settrace(None) + globals = self.curframe.f_globals + locals = self.curframe.f_locals + p = Pdb() + p.prompt = "(%s) " % self.prompt.strip() + print "ENTERING RECURSIVE DEBUGGER" + sys.call_tracing(p.run, (arg, globals, locals)) + print "LEAVING RECURSIVE DEBUGGER" + sys.settrace(self.trace_dispatch) + self.lastcmd = p.lastcmd + + def dont_debug(self, arg): + locals = self.curframe.f_locals + globals = self.curframe.f_globals + try: + r = sys.call_tracing(eval, (arg, globals, locals)) + print "--- DEBUG RETURNED ---" + if r is not None: + print repr(r) + except: + t, v = sys.exc_info()[:2] + if type(t) == type(''): + exc_type_name = t + else: exc_type_name = t.__name__ + print '***', exc_type_name + ':', v + def do_quit(self, arg): self.set_quit() return 1 @@ -834,6 +861,12 @@ Continue execution, only stop when a breakpoint is encountered.""" print """j(ump) lineno Set the next line that will be executed.""" + def help_debug(self): + print """debug code +Enter a recursive debugger that steps through the code argument +(which is an arbitrary expression or statement to be executed +in the current environment).""" + def help_list(self): self.help_l() diff --git a/Misc/NEWS b/Misc/NEWS index 3ded09e8808..661c2ca8c37 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -49,6 +49,9 @@ Core and builtins Extension modules ----------------- +- New function sys.call_tracing() allows pdb to debug code + recursively. + - New function gc.get_referents(obj) returns a list of objects directly referenced by obj. In effect, it exposes what the object's tp_traverse slot does, and can be helpful when debugging memory @@ -86,6 +89,9 @@ Extension modules Library ------- +- pdb has a new command, "debug", which lets you step through + arbitrary code from the debugger's (pdb) prompt. + - unittest.failUnlessEqual and its equivalent unittest.assertEqual now return 'not a == b' rather than 'a != b'. This gives the desired result for classes that define __eq__ without defining __ne__. diff --git a/Python/ceval.c b/Python/ceval.c index f965d3866bb..080b3c16bc3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3024,6 +3024,24 @@ call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, return result; } +PyObject * +_PyEval_CallTracing(PyObject *func, PyObject *args) +{ + PyFrameObject *frame = PyEval_GetFrame(); + PyThreadState *tstate = frame->f_tstate; + int save_tracing = tstate->tracing; + int save_use_tracing = tstate->use_tracing; + PyObject *result; + + tstate->tracing = 0; + tstate->use_tracing = ((tstate->c_tracefunc != NULL) + || (tstate->c_profilefunc != NULL)); + result = PyObject_Call(func, args, NULL); + tstate->tracing = save_tracing; + tstate->use_tracing = save_use_tracing; + return result; +} + static int maybe_call_line_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame, int *instr_lb, int *instr_ub) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index fa7f3c4a544..50b99127a01 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -17,6 +17,7 @@ Data members: #include "Python.h" #include "compile.h" #include "frameobject.h" +#include "eval.h" #include "osdefs.h" @@ -609,6 +610,23 @@ sys_getframe(PyObject *self, PyObject *args) return (PyObject*)f; } +PyDoc_STRVAR(call_tracing_doc, +"call_tracing(func, args) -> object\n\ +\n\ +Call func(*args), while tracing is enabled. The tracing state is\n\ +saved, and restored afterwards. This is intended to be called from\n\ +a debugger from a checkpoint, to recursively debug some other code." +); + +static PyObject * +sys_call_tracing(PyObject *self, PyObject *args) +{ + PyObject *func, *funcargs; + if (!PyArg_ParseTuple(args, "OO:call_tracing", &func, &funcargs)) + return NULL; + return _PyEval_CallTracing(func, funcargs); +} + PyDoc_STRVAR(callstats_doc, "callstats() -> tuple of integers\n\ \n\ @@ -700,6 +718,7 @@ static PyMethodDef sys_methods[] = { {"setrecursionlimit", sys_setrecursionlimit, METH_VARARGS, setrecursionlimit_doc}, {"settrace", sys_settrace, METH_O, settrace_doc}, + {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, {NULL, NULL} /* sentinel */ };