mirror of https://github.com/python/cpython
Patch #1680961: remove sys.exitfunc and replace it with a private C API. Also, reimplement atexit in C so it can take advantage of this private API.
This commit is contained in:
parent
450ee81b22
commit
670e692134
|
@ -1,32 +1,21 @@
|
|||
\section{\module{atexit} ---
|
||||
Exit handlers}
|
||||
|
||||
\declaremodule{standard}{atexit}
|
||||
\declaremodule{builtin}{atexit}
|
||||
\moduleauthor{Skip Montanaro}{skip@mojam.com}
|
||||
\sectionauthor{Skip Montanaro}{skip@mojam.com}
|
||||
\modulesynopsis{Register and execute cleanup functions.}
|
||||
|
||||
\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.
|
||||
|
||||
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.
|
||||
The \module{atexit} module defines functions to register and
|
||||
unregister cleanup functions. Functions thus registered are
|
||||
automatically executed upon normal interpreter termination.
|
||||
|
||||
This is an alternate interface to the functionality provided by the
|
||||
\code{sys.exitfunc} variable.
|
||||
\withsubitem{(in sys)}{\ttindex{exitfunc}}
|
||||
|
||||
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}.
|
||||
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.
|
||||
|
||||
\begin{funcdesc}{register}{func\optional{, *args\optional{, **kargs}}}
|
||||
Register \var{func} as a function to be executed at termination. Any
|
||||
|
@ -47,7 +36,16 @@ chance to run the last exception to be raised is re-raised.
|
|||
|
||||
\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}
|
||||
original name to \code{None}]{2.6}
|
||||
\end{funcdesc}
|
||||
|
||||
\begin{funcdesc}{unregister}{func}
|
||||
Remove a function \var{func} from the list of functions to be run at
|
||||
interpreter-shutdown. After calling \function{unregister()},
|
||||
\var{func} is guaranteed not to be called when the interpreter
|
||||
shuts down.
|
||||
|
||||
\versionadded{3.0}
|
||||
\end{funcdesc}
|
||||
|
||||
|
||||
|
|
|
@ -218,19 +218,6 @@ It is always available.
|
|||
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()}.
|
||||
|
|
|
@ -69,6 +69,10 @@ PyAPI_FUNC(void) PyErr_Print(void);
|
|||
PyAPI_FUNC(void) PyErr_PrintEx(int);
|
||||
PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
|
||||
|
||||
/* Py_PyAtExit is for the atexit module, Py_AtExit is for low-level
|
||||
* exit functions.
|
||||
*/
|
||||
PyAPI_FUNC(void) _Py_PyAtExit(void (*func)(void));
|
||||
PyAPI_FUNC(int) Py_AtExit(void (*func)(void));
|
||||
|
||||
PyAPI_FUNC(void) Py_Exit(int);
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
"""
|
||||
atexit.py - allow programmer to define multiple exit functions to be executed
|
||||
upon normal program termination.
|
||||
|
||||
One public function, register, is defined.
|
||||
"""
|
||||
|
||||
__all__ = ["register"]
|
||||
|
||||
import sys
|
||||
|
||||
_exithandlers = []
|
||||
def _run_exitfuncs():
|
||||
"""run any registered exit functions
|
||||
|
||||
_exithandlers is traversed in reverse order so functions are executed
|
||||
last in, first out.
|
||||
"""
|
||||
|
||||
exc_info = None
|
||||
while _exithandlers:
|
||||
func, targs, kargs = _exithandlers.pop()
|
||||
try:
|
||||
func(*targs, **kargs)
|
||||
except SystemExit:
|
||||
exc_info = sys.exc_info()
|
||||
except:
|
||||
import traceback
|
||||
print("Error in atexit._run_exitfuncs:", file=sys.stderr)
|
||||
traceback.print_exc()
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
if exc_info is not None:
|
||||
raise exc_info[0], exc_info[1], exc_info[2]
|
||||
|
||||
|
||||
def register(func, *targs, **kargs):
|
||||
"""register a function to be executed upon normal program termination
|
||||
|
||||
func - function to be called at exit
|
||||
targs - optional arguments to pass to func
|
||||
kargs - optional keyword arguments to pass to func
|
||||
|
||||
func is returned to facilitate usage as a decorator.
|
||||
"""
|
||||
_exithandlers.append((func, targs, kargs))
|
||||
return func
|
||||
|
||||
if hasattr(sys, "exitfunc"):
|
||||
# Assume it's another registered exit function - append it to our list
|
||||
register(sys.exitfunc)
|
||||
sys.exitfunc = _run_exitfuncs
|
||||
|
||||
if __name__ == "__main__":
|
||||
def x1():
|
||||
print("running x1")
|
||||
def x2(n):
|
||||
print("running x2(%r)" % (n,))
|
||||
def x3(n, kwd=None):
|
||||
print("running x3(%r, kwd=%r)" % (n, kwd))
|
||||
|
||||
register(x1)
|
||||
register(x2, 12)
|
||||
register(x3, 5, "bar")
|
||||
register(x3, "no kwd args")
|
|
@ -48,7 +48,6 @@ class AllTest(unittest.TestCase):
|
|||
self.check_all("StringIO")
|
||||
self.check_all("UserString")
|
||||
self.check_all("aifc")
|
||||
self.check_all("atexit")
|
||||
self.check_all("audiodev")
|
||||
self.check_all("base64")
|
||||
self.check_all("bdb")
|
||||
|
|
|
@ -4,97 +4,112 @@ import StringIO
|
|||
import atexit
|
||||
from test import test_support
|
||||
|
||||
### helpers
|
||||
def h1():
|
||||
print("h1")
|
||||
|
||||
def h2():
|
||||
print("h2")
|
||||
|
||||
def h3():
|
||||
print("h3")
|
||||
|
||||
def h4(*args, **kwargs):
|
||||
print("h4", args, kwargs)
|
||||
|
||||
def raise1():
|
||||
raise TypeError
|
||||
|
||||
def raise2():
|
||||
raise SystemError
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.stream = StringIO.StringIO()
|
||||
sys.stdout = sys.stderr = self.stream
|
||||
atexit._clear()
|
||||
|
||||
def tearDown(self):
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
atexit._clear()
|
||||
|
||||
def test_args(self):
|
||||
# be sure args are handled properly
|
||||
s = StringIO.StringIO()
|
||||
sys.stdout = sys.stderr = s
|
||||
save_handlers = atexit._exithandlers
|
||||
atexit._exithandlers = []
|
||||
try:
|
||||
atexit.register(self.h1)
|
||||
atexit.register(self.h4)
|
||||
atexit.register(self.h4, 4, kw="abc")
|
||||
atexit._run_exitfuncs()
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
atexit._exithandlers = save_handlers
|
||||
self.assertEqual(s.getvalue(), "h4 (4,) {'kw': 'abc'}\nh4 () {}\nh1\n")
|
||||
atexit.register(h1)
|
||||
atexit.register(h4)
|
||||
atexit.register(h4, 4, kw="abc")
|
||||
atexit._run_exitfuncs()
|
||||
|
||||
self.assertEqual(self.stream.getvalue(),
|
||||
"h4 (4,) {'kw': 'abc'}\nh4 () {}\nh1\n")
|
||||
|
||||
def test_order(self):
|
||||
# be sure handlers are executed in reverse order
|
||||
s = StringIO.StringIO()
|
||||
sys.stdout = sys.stderr = s
|
||||
save_handlers = atexit._exithandlers
|
||||
atexit._exithandlers = []
|
||||
try:
|
||||
atexit.register(self.h1)
|
||||
atexit.register(self.h2)
|
||||
atexit.register(self.h3)
|
||||
atexit._run_exitfuncs()
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
atexit._exithandlers = save_handlers
|
||||
self.assertEqual(s.getvalue(), "h3\nh2\nh1\n")
|
||||
|
||||
def test_sys_override(self):
|
||||
# be sure a preset sys.exitfunc is handled properly
|
||||
s = StringIO.StringIO()
|
||||
sys.stdout = sys.stderr = s
|
||||
save_handlers = atexit._exithandlers
|
||||
atexit._exithandlers = []
|
||||
exfunc = sys.exitfunc
|
||||
sys.exitfunc = self.h1
|
||||
reload(atexit)
|
||||
try:
|
||||
atexit.register(self.h2)
|
||||
atexit._run_exitfuncs()
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
atexit._exithandlers = save_handlers
|
||||
sys.exitfunc = exfunc
|
||||
self.assertEqual(s.getvalue(), "h2\nh1\n")
|
||||
atexit.register(h1)
|
||||
atexit.register(h2)
|
||||
atexit.register(h3)
|
||||
atexit._run_exitfuncs()
|
||||
|
||||
self.assertEqual(self.stream.getvalue(), "h3\nh2\nh1\n")
|
||||
|
||||
def test_raise(self):
|
||||
# be sure raises are handled properly
|
||||
s = StringIO.StringIO()
|
||||
sys.stdout = sys.stderr = s
|
||||
save_handlers = atexit._exithandlers
|
||||
atexit._exithandlers = []
|
||||
try:
|
||||
atexit.register(self.raise1)
|
||||
atexit.register(self.raise2)
|
||||
self.assertRaises(TypeError, atexit._run_exitfuncs)
|
||||
finally:
|
||||
sys.stdout = sys.__stdout__
|
||||
sys.stderr = sys.__stderr__
|
||||
atexit._exithandlers = save_handlers
|
||||
|
||||
### helpers
|
||||
def h1(self):
|
||||
print("h1")
|
||||
|
||||
def h2(self):
|
||||
print("h2")
|
||||
|
||||
def h3(self):
|
||||
print("h3")
|
||||
|
||||
def h4(self, *args, **kwargs):
|
||||
print("h4", args, kwargs)
|
||||
|
||||
def raise1(self):
|
||||
raise TypeError
|
||||
|
||||
def raise2(self):
|
||||
raise SystemError
|
||||
atexit.register(raise1)
|
||||
atexit.register(raise2)
|
||||
|
||||
self.assertRaises(TypeError, atexit._run_exitfuncs)
|
||||
|
||||
def test_stress(self):
|
||||
a = [0]
|
||||
def inc():
|
||||
a[0] += 1
|
||||
|
||||
for i in range(128):
|
||||
atexit.register(inc)
|
||||
atexit._run_exitfuncs()
|
||||
|
||||
self.assertEqual(a[0], 128)
|
||||
|
||||
def test_clear(self):
|
||||
a = [0]
|
||||
def inc():
|
||||
a[0] += 1
|
||||
|
||||
atexit.register(inc)
|
||||
atexit._clear()
|
||||
atexit._run_exitfuncs()
|
||||
|
||||
self.assertEqual(a[0], 0)
|
||||
|
||||
def test_unregister(self):
|
||||
a = [0]
|
||||
def inc():
|
||||
a[0] += 1
|
||||
def dec():
|
||||
a[0] -= 1
|
||||
|
||||
for i in range(4):
|
||||
atexit.register(inc)
|
||||
atexit.register(dec)
|
||||
atexit.unregister(inc)
|
||||
atexit._run_exitfuncs()
|
||||
|
||||
self.assertEqual(a[0], -1)
|
||||
|
||||
def test_bound_methods(self):
|
||||
l = []
|
||||
atexit.register(l.append, 5)
|
||||
atexit._run_exitfuncs()
|
||||
self.assertEqual(l, [5])
|
||||
|
||||
atexit.unregister(l.append)
|
||||
atexit._run_exitfuncs()
|
||||
self.assertEqual(l, [5])
|
||||
|
||||
|
||||
def test_main():
|
||||
test_support.run_unittest(TestCase)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
||||
|
|
|
@ -28,6 +28,9 @@ TO DO
|
|||
Core and Builtins
|
||||
-----------------
|
||||
|
||||
- Patch #1680961: sys.exitfunc has been removed and replaced with a private
|
||||
C-level API.
|
||||
|
||||
- PEP 3115: new metaclasses: the metaclass is now specified as a
|
||||
keyword arg in the class statement, which can now use the full syntax of
|
||||
a parameter list. Also, the metaclass can implement a __prepare__ function
|
||||
|
@ -156,6 +159,8 @@ Extension Modules
|
|||
Library
|
||||
-------
|
||||
|
||||
- Patch #1680961: atexit has been reimplemented in C.
|
||||
|
||||
- Removed all traces of the sets module.
|
||||
|
||||
Build
|
||||
|
|
|
@ -176,6 +176,7 @@ GLHACK=-Dclear=__GLclear
|
|||
#collections collectionsmodule.c # Container types
|
||||
#itertools itertoolsmodule.c # Functions creating iterators for efficient looping
|
||||
#strop stropmodule.c # String manipulations
|
||||
#atexit atexitmodule.c # Register functions to be run at interpreter-shutdown
|
||||
|
||||
#unicodedata unicodedata.c # static Unicode character database
|
||||
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* atexit - allow programmer to define multiple exit functions to be executed
|
||||
* upon normal program termination.
|
||||
*
|
||||
* Translated from atexit.py by Collin Winter.
|
||||
+ Copyright 2007 Python Software Foundation.
|
||||
*/
|
||||
|
||||
#include "Python.h"
|
||||
|
||||
/* ===================================================================== */
|
||||
/* Callback machinery. */
|
||||
|
||||
typedef struct {
|
||||
PyObject *func;
|
||||
PyObject *args;
|
||||
PyObject *kwargs;
|
||||
} atexit_callback;
|
||||
|
||||
atexit_callback **atexit_callbacks;
|
||||
int ncallbacks = 0;
|
||||
int callback_len = 32;
|
||||
|
||||
/* Installed into pythonrun.c's atexit mechanism */
|
||||
|
||||
void
|
||||
atexit_callfuncs(void)
|
||||
{
|
||||
PyObject *exc_type = NULL, *exc_value, *exc_tb, *r;
|
||||
atexit_callback *cb;
|
||||
int i;
|
||||
|
||||
if (ncallbacks == 0)
|
||||
return;
|
||||
|
||||
for(i = ncallbacks - 1; i >= 0; i--)
|
||||
{
|
||||
cb = atexit_callbacks[i];
|
||||
if (cb == NULL)
|
||||
continue;
|
||||
|
||||
r = PyObject_Call(cb->func, cb->args, cb->kwargs);
|
||||
Py_XDECREF(r);
|
||||
if (r == NULL) {
|
||||
if (exc_type) {
|
||||
Py_DECREF(exc_type);
|
||||
Py_DECREF(exc_value);
|
||||
Py_DECREF(exc_tb);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exc_type)
|
||||
PyErr_Restore(exc_type, exc_value, exc_tb);
|
||||
}
|
||||
|
||||
void
|
||||
atexit_delete_cb(int i)
|
||||
{
|
||||
atexit_callback *cb = atexit_callbacks[i];
|
||||
atexit_callbacks[i] = NULL;
|
||||
Py_DECREF(cb->func);
|
||||
Py_DECREF(cb->args);
|
||||
Py_XDECREF(cb->kwargs);
|
||||
PyMem_Free(cb);
|
||||
}
|
||||
|
||||
/* ===================================================================== */
|
||||
/* Module methods. */
|
||||
|
||||
PyDoc_STRVAR(atexit_register__doc__,
|
||||
"register(func, *args, **kwargs) -> func\n\
|
||||
\n\
|
||||
Register a function to be executed upon normal program termination\n\
|
||||
\n\
|
||||
func - function to be called at exit\n\
|
||||
args - optional arguments to pass to func\n\
|
||||
kwargs - 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 *kwargs)
|
||||
{
|
||||
atexit_callback *new_callback;
|
||||
PyObject *func = NULL;
|
||||
|
||||
if (ncallbacks >= callback_len) {
|
||||
callback_len += 16;
|
||||
atexit_callbacks = PyMem_Realloc(atexit_callbacks,
|
||||
sizeof(atexit_callback*) * callback_len);
|
||||
|
||||
}
|
||||
|
||||
if (PyTuple_GET_SIZE(args) == 0) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"register() takes at least 1 argument (0 given)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
func = PyTuple_GET_ITEM(args, 0);
|
||||
if (!PyCallable_Check(func)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"the first argument must be callable");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_callback = PyMem_Malloc(sizeof(atexit_callback));
|
||||
if (new_callback == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
new_callback->args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
|
||||
if (new_callback->args == NULL) {
|
||||
PyMem_Free(new_callback);
|
||||
return NULL;
|
||||
}
|
||||
new_callback->func = func;
|
||||
new_callback->kwargs = kwargs;
|
||||
Py_INCREF(func);
|
||||
Py_XINCREF(kwargs);
|
||||
|
||||
atexit_callbacks[ncallbacks++] = new_callback;
|
||||
|
||||
Py_INCREF(func);
|
||||
return func;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
atexit_run_exitfuncs(PyObject *self)
|
||||
{
|
||||
atexit_callfuncs();
|
||||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
atexit_clear(PyObject *self)
|
||||
{
|
||||
atexit_callback *cb;
|
||||
int i;
|
||||
|
||||
for(i = 0; i < ncallbacks; i++)
|
||||
{
|
||||
cb = atexit_callbacks[i];
|
||||
if (cb == NULL)
|
||||
continue;
|
||||
|
||||
atexit_delete_cb(i);
|
||||
}
|
||||
ncallbacks = 0;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
atexit_unregister(PyObject *self, PyObject *func)
|
||||
{
|
||||
atexit_callback *cb;
|
||||
int i, eq;
|
||||
|
||||
for(i = 0; i < ncallbacks; i++)
|
||||
{
|
||||
cb = atexit_callbacks[i];
|
||||
if (cb == NULL)
|
||||
continue;
|
||||
|
||||
eq = PyObject_RichCompareBool(cb->func, func, Py_EQ);
|
||||
if (eq < 0)
|
||||
return NULL;
|
||||
if (eq)
|
||||
atexit_delete_cb(i);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef atexit_methods[] = {
|
||||
{"register", (PyCFunction) atexit_register, METH_VARARGS|METH_KEYWORDS,
|
||||
atexit_register__doc__},
|
||||
{"_clear", (PyCFunction) atexit_clear, METH_NOARGS,
|
||||
NULL},
|
||||
{"unregister", (PyCFunction) atexit_unregister, METH_O,
|
||||
NULL},
|
||||
{"_run_exitfuncs", (PyCFunction) atexit_run_exitfuncs, METH_NOARGS,
|
||||
NULL},
|
||||
{NULL, NULL} /* sentinel */
|
||||
};
|
||||
|
||||
/* ===================================================================== */
|
||||
/* Initialization function. */
|
||||
|
||||
PyDoc_STRVAR(atexit__doc__,
|
||||
"atexit.py - allow programmer to define multiple exit functions to be executed\
|
||||
upon normal program termination.\n\
|
||||
\n\
|
||||
One public function, register, is defined.\n\
|
||||
");
|
||||
|
||||
PyMODINIT_FUNC
|
||||
initatexit(void)
|
||||
{
|
||||
PyObject *m;
|
||||
|
||||
atexit_callbacks = PyMem_New(atexit_callback*, callback_len);
|
||||
if (atexit_callbacks == NULL)
|
||||
return;
|
||||
|
||||
m = Py_InitModule3("atexit", atexit_methods, atexit__doc__);
|
||||
if (m == NULL)
|
||||
return;
|
||||
|
||||
_Py_PyAtExit(atexit_callfuncs);
|
||||
}
|
|
@ -361,7 +361,7 @@ PyImport_GetModuleDict(void)
|
|||
|
||||
/* 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",
|
||||
|
|
|
@ -56,7 +56,7 @@ static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *,
|
|||
PyCompilerFlags *);
|
||||
static void err_input(perrdetail *);
|
||||
static void initsigs(void);
|
||||
static void call_sys_exitfunc(void);
|
||||
static void call_py_exitfuncs(void);
|
||||
static void call_ll_exitfuncs(void);
|
||||
extern void _PyUnicode_Init(void);
|
||||
extern void _PyUnicode_Fini(void);
|
||||
|
@ -355,7 +355,7 @@ Py_Finalize(void)
|
|||
* threads created thru it, so this also protects pending imports in
|
||||
* the threads created via Threading.
|
||||
*/
|
||||
call_sys_exitfunc();
|
||||
call_py_exitfuncs();
|
||||
initialized = 0;
|
||||
|
||||
/* Get current thread state and interpreter pointer */
|
||||
|
@ -1557,6 +1557,23 @@ Py_FatalError(const char *msg)
|
|||
#include "pythread.h"
|
||||
#endif
|
||||
|
||||
static void (*pyexitfunc)(void) = NULL;
|
||||
/* For the atexit module. */
|
||||
void _Py_PyAtExit(void (*func)(void))
|
||||
{
|
||||
pyexitfunc = func;
|
||||
}
|
||||
|
||||
static void
|
||||
call_py_exitfuncs(void)
|
||||
{
|
||||
if (pyexitfunc == NULL)
|
||||
return;
|
||||
|
||||
(*pyexitfunc)();
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
#define NEXITFUNCS 32
|
||||
static void (*exitfuncs[NEXITFUNCS])(void);
|
||||
static int nexitfuncs = 0;
|
||||
|
@ -1569,27 +1586,6 @@ int Py_AtExit(void (*func)(void))
|
|||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -897,9 +897,6 @@ excepthook -- called to handle any uncaught exception other than SystemExit\n\
|
|||
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\
|
||||
|
|
2
setup.py
2
setup.py
|
@ -379,6 +379,8 @@ class PyBuildExt(build_ext):
|
|||
exts.append( Extension('operator', ['operator.c']) )
|
||||
# _functools
|
||||
exts.append( Extension("_functools", ["_functoolsmodule.c"]) )
|
||||
# atexit
|
||||
exts.append( Extension("atexit", ["atexitmodule.c"]) )
|
||||
# Python C API test module
|
||||
exts.append( Extension('_testcapi', ['_testcapimodule.c']) )
|
||||
# profilers (_lsprof is for cProfile.py)
|
||||
|
|
Loading…
Reference in New Issue