Index: Python/pythonrun.c =================================================================== --- Python/pythonrun.c (Revision 54265) +++ Python/pythonrun.c (Arbeitskopie) @@ -56,7 +56,7 @@ PyCompilerFlags *); static void err_input(perrdetail *); static void initsigs(void); -static void call_sys_exitfunc(void); +static void call_python_exitfunc(void); static void call_ll_exitfuncs(void); extern void _PyUnicode_Init(void); extern void _PyUnicode_Fini(void); @@ -355,7 +355,7 @@ * threads created thru it, so this also protects pending imports in * the threads created via Threading. */ - call_sys_exitfunc(); + call_python_exitfunc(); initialized = 0; /* Get current thread state and interpreter pointer */ @@ -1561,7 +1561,10 @@ static void (*exitfuncs[NEXITFUNCS])(void); static int nexitfuncs = 0; -int Py_AtExit(void (*func)(void)) +static PyObject * (*python_exitfunc)(PyObject *) = NULL; + +int +Py_AtExit(void (*func)(void)) { if (nexitfuncs >= NEXITFUNCS) return -1; @@ -1570,27 +1573,6 @@ } static void -call_sys_exitfunc(void) -{ - PyObject *exitfunc = PySys_GetObject("exitfunc"); - - if (exitfunc) { - PyObject *res; - Py_INCREF(exitfunc); - PySys_SetObject("exitfunc", (PyObject *)NULL); - res = PyEval_CallObject(exitfunc, (PyObject *)NULL); - if (res == NULL) { - if (!PyErr_ExceptionMatches(PyExc_SystemExit)) { - PySys_WriteStderr("Error in sys.exitfunc:\n"); - } - PyErr_Print(); - } - Py_DECREF(exitfunc); - } - -} - -static void call_ll_exitfuncs(void) { while (nexitfuncs > 0) @@ -1600,7 +1582,31 @@ fflush(stderr); } +/* For the atexit module. This used to be sys.exitfunc, but it's no longer + directly settable from Python code. */ void +Py_SetPythonExitfunc(PyObject * (*func)(PyObject *)) +{ + python_exitfunc = func; +} + +static void +call_python_exitfunc(void) +{ + PyObject *res; + if (!python_exitfunc) + return; + res = (*python_exitfunc)(NULL); + if (res == NULL) { + if (!PyErr_ExceptionMatches(PyExc_SystemExit)) { + PySys_WriteStderr("Error in exitfunc:\n"); + } + PyErr_Print(); + } +} + + +void Py_Exit(int sts) { Py_Finalize(); Index: Python/import.c =================================================================== --- Python/import.c (Revision 54265) +++ Python/import.c (Arbeitskopie) @@ -360,7 +360,7 @@ /* List of names to clear in sys */ static char* sys_deletes[] = { - "path", "argv", "ps1", "ps2", "exitfunc", + "path", "argv", "ps1", "ps2", "exc_type", "exc_value", "exc_traceback", "last_type", "last_value", "last_traceback", "path_hooks", "path_importer_cache", "meta_path", Index: Python/sysmodule.c =================================================================== --- Python/sysmodule.c (Revision 54265) +++ Python/sysmodule.c (Arbeitskopie) @@ -897,9 +897,6 @@ To customize printing in an interactive session or to install a custom\n\ top-level exception handler, assign other functions to replace these.\n\ \n\ -exitfunc -- if sys.exitfunc exists, this routine is called when Python exits\n\ - Assigning to sys.exitfunc is deprecated; use the atexit module instead.\n\ -\n\ stdin -- standard input file object; used by raw_input() and input()\n\ stdout -- standard output file object; used by print()\n\ stderr -- standard error object; used for error messages\n\ Index: Include/pythonrun.h =================================================================== --- Include/pythonrun.h (Revision 54265) +++ Include/pythonrun.h (Arbeitskopie) @@ -70,6 +70,7 @@ PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) Py_AtExit(void (*func)(void)); +PyAPI_FUNC(void) Py_SetPythonExitfunc(PyObject * (*func)(PyObject *)); PyAPI_FUNC(void) Py_Exit(int); Index: Doc/lib/libatexit.tex =================================================================== --- Doc/lib/libatexit.tex (Revision 54265) +++ Doc/lib/libatexit.tex (Arbeitskopie) @@ -8,46 +8,41 @@ \versionadded{2.0} -The \module{atexit} module defines a single function to register -cleanup functions. Functions thus registered are automatically -executed upon normal interpreter termination. +The \module{atexit} module defines a single function to register cleanup +functions. Functions thus registered are automatically executed upon normal +interpreter termination. -Note: the functions registered via this module are not called when the program is killed by a -signal, when a Python fatal internal error is detected, or when +Note: the functions registered via this module are not called when the program +is killed by a signal, when a Python fatal internal error is detected, or when \function{os._exit()} is called. -This is an alternate interface to the functionality provided by the -\code{sys.exitfunc} variable. -\withsubitem{(in sys)}{\ttindex{exitfunc}} +\begin{funcdesc}{register}{func\optional{, *args\optional{, **kargs}}} + Register \var{func} as a function to be executed at termination. Any optional + arguments that are to be passed to \var{func} must be passed as arguments to + \function{register()}. -Note: This module is unlikely to work correctly when used with other code -that sets \code{sys.exitfunc}. In particular, other core Python modules are -free to use \module{atexit} without the programmer's knowledge. Authors who -use \code{sys.exitfunc} should convert their code to use -\module{atexit} instead. The simplest way to convert code that sets -\code{sys.exitfunc} is to import \module{atexit} and register the function -that had been bound to \code{sys.exitfunc}. + At normal program termination (for instance, if \function{sys.exit()} is + called or the main module's execution completes), all functions registered are + called in last in, first out order. The assumption is that lower level + modules will normally be imported before higher level modules and thus must be + cleaned up later. -\begin{funcdesc}{register}{func\optional{, *args\optional{, **kargs}}} -Register \var{func} as a function to be executed at termination. Any -optional arguments that are to be passed to \var{func} must be passed -as arguments to \function{register()}. + If an exception is raised during execution of the exit handlers, a traceback + is printed (unless \exception{SystemExit} is raised) and the exception + information is saved. After all exit handlers have had a chance to run the + last exception to be raised is re-raised. -At normal program termination (for instance, if -\function{sys.exit()} is called or the main module's execution -completes), all functions registered are called in last in, first out -order. The assumption is that lower level modules will normally be -imported before higher level modules and thus must be cleaned up -later. + \versionchanged[This function now returns \var{func} which makes it + possible to use it as a decorator without binding the + original name to \code{None}]{2.6} +\end{funcdesc} -If an exception is raised during execution of the exit handlers, a -traceback is printed (unless \exception{SystemExit} is raised) and the -exception information is saved. After all exit handlers have had a -chance to run the last exception to be raised is re-raised. +\begin{funcdesc}{get_handlers}{} + Return a list of exit handlers registered by \function{register()}. + These are tuples in the form \code{(func, args, kwds)}. -\versionchanged[This function now returns \var{func} which makes it - possible to use it as a decorator without binding the - original name to \code{None}]{2.6} + This is only meant as a way to inspect the exit handlers, the list + should not be changed. \end{funcdesc} Index: Doc/lib/libsys.tex =================================================================== --- Doc/lib/libsys.tex (Revision 54265) +++ Doc/lib/libsys.tex (Arbeitskopie) @@ -218,19 +218,6 @@ program when an error occurs. \end{funcdesc} -\begin{datadesc}{exitfunc} - This value is not actually defined by the module, but can be set by - the user (or by a program) to specify a clean-up action at program - exit. When set, it should be a parameterless function. This - function will be called when the interpreter exits. Only one - function may be installed in this way; to allow multiple functions - which will be called at termination, use the \refmodule{atexit} - module. \note{The exit function is not called when the program is - killed by a signal, when a Python fatal internal error is detected, - or when \code{os._exit()} is called.} - \deprecated{2.4}{Use \refmodule{atexit} instead.} -\end{datadesc} - \begin{funcdesc}{getcheckinterval}{} Return the interpreter's ``check interval''; see \function{setcheckinterval()}. Index: Modules/atexit.c =================================================================== --- Modules/atexit.c (Revision 0) +++ Modules/atexit.c (Revision 0) @@ -0,0 +1,163 @@ +/* atexit - allow programmer to define multiple exit functions to be executed + upon normal program termination. + + One public function, register, is defined. + + Translated from atexit.py by Georg Brandl. + Copyright 2007 Python Software Foundation. +*/ + +#include "Python.h" + +static PyObject *atexit_exithandlers = NULL; + + +PyDoc_STRVAR(atexit__doc__, +"This module allows the programmer to define multiple exit functions\n\ +to be executed upon normal program termination.\n\ +\n\ +One public function, register, is defined."); + + +PyDoc_STRVAR(_run_exitfuncs__doc__, +"run any registered exit functions\n\ +\n\ + _exithandlers is traversed in reverse order so functions are executed\n\ + last in, first out."); + +static PyObject * +atexit_run_exitfuncs(PyObject *self) +{ + PyObject *tmp, *seq, *res, *kwds; + PyObject *exc_type, *exc_value, *exc_tb; + + assert(PyList_Check(atexit_exithandlers)); + + while (PyList_GET_SIZE(atexit_exithandlers)) { + tmp = PyObject_CallMethod(atexit_exithandlers, "pop", NULL); + if (tmp == NULL) + return NULL; + seq = PySequence_Fast(tmp, "handler items must be an iterable of " + "(func, args, kwds)"); + Py_DECREF(tmp); + if (seq == NULL) + return NULL; + if (PySequence_Fast_GET_SIZE(seq) != 3) { + PyErr_SetString(PyExc_ValueError, + "handler items must have exactly three elements"); + Py_DECREF(seq); + return NULL; + } + kwds = PySequence_Fast_GET_ITEM(seq, 2); + if (kwds == Py_None) + kwds = NULL; + res = PyObject_Call(PySequence_Fast_GET_ITEM(seq, 0), + PySequence_Fast_GET_ITEM(seq, 1), + kwds); + Py_DECREF(seq); + if (res == NULL) { + PyErr_Fetch(&exc_type, &exc_value, &exc_tb); + if (!PyErr_ExceptionMatches(PyExc_SystemExit)) { + PySys_WriteStderr("Error in atexit._run_exitfuncs:\n"); + PyErr_Display(exc_type, exc_value, exc_tb); + } + } else { + Py_DECREF(res); + } + } + if (exc_type) { + PyErr_Restore(exc_type, exc_value, exc_tb); + return NULL; + } + Py_RETURN_NONE; +} + + +PyDoc_STRVAR(get_handlers__doc__, +"Return the list of registered exit handlers, which are tuples of\n\ +(func, args, kwds)."); + +static PyObject * +atexit_get_handlers(PyObject *self) +{ + /* XXX: make a copy instead? */ + Py_INCREF(atexit_exithandlers); + return atexit_exithandlers; +} + + +PyDoc_STRVAR(register__doc__, +"register(func, *targs, **kargs): register a function to be executed\n\ +upon normal program termination\n\ +\n\ + func - function to be called at exit\n\ + targs - optional arguments to pass to func\n\ + kargs - optional keyword arguments to pass to func\n\ +\n\ + func is returned to facilitate usage as a decorator."); + +static PyObject * +atexit_register(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyObject *func, *pargs, *tup; + int err; + + /* function is args[0] */ + assert(PyTuple_Check(args)); + if (PyTuple_GET_SIZE(args) == 0) { + PyErr_SetString(PyExc_TypeError, + "register() takes at least 1 argument (0 given)"); + return NULL; + } + + if (atexit_exithandlers == NULL) { + atexit_exithandlers = PyList_New(0); + if (atexit_exithandlers == NULL) + return NULL; + Py_SetPythonExitfunc(atexit_run_exitfuncs); + } + + tup = PyTuple_New(3); + if (tup == NULL) + return NULL; + func = PyTuple_GET_ITEM(args, 0); + Py_INCREF(func); + PyTuple_SET_ITEM(tup, 0, func); + + pargs = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args)); + if (pargs == NULL) { + Py_DECREF(tup); + return NULL; + } + PyTuple_SET_ITEM(tup, 1, pargs); + + if (kwds == NULL) + kwds = Py_None; + Py_INCREF(kwds); + PyTuple_SET_ITEM(tup, 2, kwds); + + err = PyList_Append(atexit_exithandlers, tup); + Py_DECREF(tup); + + if (err == -1) + return NULL; + Py_INCREF(func); + return func; +} + +static PyMethodDef atexit_methods[] = { + {"register", (PyCFunction)atexit_register, METH_VARARGS|METH_KEYWORDS, + register__doc__}, + {"get_handlers", (PyCFunction)atexit_get_handlers, METH_NOARGS, + get_handlers__doc__}, + {"_run_exitfuncs", (PyCFunction)atexit_run_exitfuncs, METH_NOARGS, + _run_exitfuncs__doc__}, + {NULL, NULL} /* sentinel */ +}; + +PyMODINIT_FUNC +initatexit(void) +{ + PyObject *m; + m = Py_InitModule3("atexit", atexit_methods, atexit__doc__); +}