diff --git a/Doc/lib/libsys.tex b/Doc/lib/libsys.tex index 3f8f7b6a777..d027aa62ac7 100644 --- a/Doc/lib/libsys.tex +++ b/Doc/lib/libsys.tex @@ -99,6 +99,11 @@ It is always available. encapsulates the call stack at the point where the exception originally occurred. \obindex{traceback} + If \function{exc_clear()} is called, this function will return three + \code{None} values until either another exception is raised in the + current thread or the execution stack returns to a frame where + another exception is being handled. + \warning{Assigning the \var{traceback} return value to a local variable in a function that is handling an exception will cause a circular reference. This will prevent anything referenced @@ -115,6 +120,21 @@ It is always available. efficient to avoid creating cycles.} \end{funcdesc} +\begin{funcdesc}{exc_clear}{} + This function clears all information relating to the current or last + exception that occured in the current thread. After calling this + function, \function{exc_info()} will return three \code{None} values until + another exception is raised in the current thread or the execution stack + returns to a frame where another exception is being handled. + + This function is only needed in only a few obscure situations. These + include logging and error handling systems that report information on the + last or current exception. This function can also be used to try to free + resources and trigger object finalization, though no guarantee is made as + to what objects will be freed, if any. +\versionadded{2.3} +\end{funcdesc} + \begin{datadesc}{exc_type} \dataline{exc_value} \dataline{exc_traceback} diff --git a/Doc/tut/tut.tex b/Doc/tut/tut.tex index cfc56f09222..a9fb325fb9a 100644 --- a/Doc/tut/tut.tex +++ b/Doc/tut/tut.tex @@ -2451,14 +2451,15 @@ a module defines. It returns a sorted list of strings: ['__name__', 'fib', 'fib2'] >>> dir(sys) ['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__', - '__stdin__', '__stdout__', '_getframe', 'argv', 'builtin_module_names', - 'byteorder', 'copyright', 'displayhook', 'exc_info', 'exc_type', - 'excepthook', 'exec_prefix', 'executable', 'exit', 'getdefaultencoding', - 'getdlopenflags', 'getrecursionlimit', 'getrefcount', 'hexversion', - 'maxint', 'maxunicode', 'modules', 'path', 'platform', 'prefix', 'ps1', - 'ps2', 'setcheckinterval', 'setdlopenflags', 'setprofile', - 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', 'version', - 'version_info', 'warnoptions'] + '__stdin__', '__stdout__', '_getframe', 'api_version', 'argv', + 'builtin_module_names', 'byteorder', 'callstats', 'copyright', + 'displayhook', 'exc_clear', 'exc_info', 'exc_type', 'excepthook', + 'exec_prefix', 'executable', 'exit', 'getdefaultencoding', 'getdlopenflags', + 'getrecursionlimit', 'getrefcount', 'hexversion', 'maxint', 'maxunicode', + 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache', + 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags', + 'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout', + 'version', 'version_info', 'warnoptions'] \end{verbatim} Without arguments, \function{dir()} lists the names you have defined diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index b18b0c7761c..2afac653ef0 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -63,6 +63,50 @@ class SysModuleTest(unittest.TestCase): # FIXME: testing the code for a lost or replaced excepthook in # Python/pythonrun.c::PyErr_PrintEx() is tricky. + def test_exc_clear(self): + self.assertRaises(TypeError, sys.exc_clear, 42) + + # Verify that exc_info is present and matches exc, then clear it, and + # check that it worked. + def clear_check(exc): + typ, value, traceback = sys.exc_info() + self.assert_(typ is not None) + self.assert_(value is exc) + self.assert_(traceback is not None) + + sys.exc_clear() + + typ, value, traceback = sys.exc_info() + self.assert_(typ is None) + self.assert_(value is None) + self.assert_(traceback is None) + + def clear(): + try: + raise ValueError, 42 + except ValueError, exc: + clear_check(exc) + + # Raise an exception and check that it can be cleared + clear() + + # Verify that a frame currently handling an exception is + # unaffected by calling exc_clear in a nested frame. + try: + raise ValueError, 13 + except ValueError, exc: + typ1, value1, traceback1 = sys.exc_info() + clear() + typ2, value2, traceback2 = sys.exc_info() + + self.assert_(typ1 is typ2) + self.assert_(value1 is exc) + self.assert_(value1 is value2) + self.assert_(traceback1 is traceback2) + + # Check that an exception can be cleared outside of an except block + clear_check(exc) + def test_exit(self): self.assertRaises(TypeError, sys.exit, 42, 42) diff --git a/Misc/NEWS b/Misc/NEWS index 678b783736f..5405ce54b1d 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -12,6 +12,12 @@ What's New in Python 2.3 beta 1? Core and builtins ----------------- + +- New function sys.exc_clear() clears the current exception. This is + rarely needed, but can sometimes be useful to release objects + referenced by the traceback held in sys.exc_info()[2]. (SF patch + #693195.) + - On 64-bit systems, a dictionary could contain duplicate long/int keys if the key value was larger than 2**32. See SF bug #689659. diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 1f51f988f9e..13b86f2270b 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -132,7 +132,7 @@ PyDoc_STRVAR(excepthook_doc, ); static PyObject * -sys_exc_info(PyObject *self) +sys_exc_info(PyObject *self, PyObject *noargs) { PyThreadState *tstate; tstate = PyThreadState_Get(); @@ -147,8 +147,39 @@ sys_exc_info(PyObject *self) PyDoc_STRVAR(exc_info_doc, "exc_info() -> (type, value, traceback)\n\ \n\ -Return information about the exception that is currently being handled.\n\ -This should be called from inside an except clause only." +Return information about the most recent exception caught by an except\n\ +clause in the current stack frame or in an older stack frame." +); + +static PyObject * +sys_exc_clear(PyObject *self, PyObject *noargs) +{ + PyThreadState *tstate = PyThreadState_Get(); + PyObject *tmp_type, *tmp_value, *tmp_tb; + tmp_type = tstate->exc_type; + tmp_value = tstate->exc_value; + tmp_tb = tstate->exc_traceback; + tstate->exc_type = NULL; + tstate->exc_value = NULL; + tstate->exc_traceback = NULL; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); + /* For b/w compatibility */ + PySys_SetObject("exc_type", Py_None); + PySys_SetObject("exc_value", Py_None); + PySys_SetObject("exc_traceback", Py_None); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(exc_clear_doc, +"exc_clear() -> None\n\ +\n\ +Clear global information on the current exception. Subsequent calls to\n\ +exc_info() will return (None,None,None) until another exception is raised\n\ +in the current thread or the execution stack returns to a frame where\n\ +another exception is being handled." ); static PyObject * @@ -600,7 +631,8 @@ static PyMethodDef sys_methods[] = { {"callstats", (PyCFunction)PyEval_GetCallStats, METH_NOARGS, callstats_doc}, {"displayhook", sys_displayhook, METH_O, displayhook_doc}, - {"exc_info", (PyCFunction)sys_exc_info, METH_NOARGS, exc_info_doc}, + {"exc_info", sys_exc_info, METH_NOARGS, exc_info_doc}, + {"exc_clear", sys_exc_clear, METH_NOARGS, exc_clear_doc}, {"excepthook", sys_excepthook, METH_VARARGS, excepthook_doc}, {"exit", sys_exit, METH_VARARGS, exit_doc}, #ifdef Py_USING_UNICODE @@ -786,6 +818,7 @@ Functions:\n\ displayhook() -- print an object to the screen, and save it in __builtin__._\n\ excepthook() -- print an exception and its traceback to sys.stderr\n\ exc_info() -- return thread-safe information about the current exception\n\ +exc_clear() -- clear the exception state for the current thread\n\ exit() -- exit the interpreter by raising SystemExit\n\ getdlopenflags() -- returns flags to be used for dlopen() calls\n\ getrefcount() -- return the reference count for an object (plus one :-)\n\