mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
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
13 changed files with 360 additions and 204 deletions
|
@ -1,32 +1,21 @@
|
||||||
\section{\module{atexit} ---
|
\section{\module{atexit} ---
|
||||||
Exit handlers}
|
Exit handlers}
|
||||||
|
|
||||||
\declaremodule{standard}{atexit}
|
\declaremodule{builtin}{atexit}
|
||||||
\moduleauthor{Skip Montanaro}{skip@mojam.com}
|
\moduleauthor{Skip Montanaro}{skip@mojam.com}
|
||||||
\sectionauthor{Skip Montanaro}{skip@mojam.com}
|
\sectionauthor{Skip Montanaro}{skip@mojam.com}
|
||||||
\modulesynopsis{Register and execute cleanup functions.}
|
\modulesynopsis{Register and execute cleanup functions.}
|
||||||
|
|
||||||
\versionadded{2.0}
|
\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
|
The \module{atexit} module defines functions to register and
|
||||||
signal, when a Python fatal internal error is detected, or when
|
unregister cleanup functions. Functions thus registered are
|
||||||
\function{os._exit()} is called.
|
automatically executed upon normal interpreter termination.
|
||||||
|
|
||||||
This is an alternate interface to the functionality provided by the
|
Note: the functions registered via this module are not called when
|
||||||
\code{sys.exitfunc} variable.
|
the program is killed by a signal, when a Python fatal internal
|
||||||
\withsubitem{(in sys)}{\ttindex{exitfunc}}
|
error is detected, or when \function{os._exit()} is called.
|
||||||
|
|
||||||
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}.
|
|
||||||
|
|
||||||
\begin{funcdesc}{register}{func\optional{, *args\optional{, **kargs}}}
|
\begin{funcdesc}{register}{func\optional{, *args\optional{, **kargs}}}
|
||||||
Register \var{func} as a function to be executed at termination. Any
|
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
|
\versionchanged[This function now returns \var{func} which makes it
|
||||||
possible to use it as a decorator without binding the
|
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}
|
\end{funcdesc}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -218,19 +218,6 @@ It is always available.
|
||||||
program when an error occurs.
|
program when an error occurs.
|
||||||
\end{funcdesc}
|
\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}{}
|
\begin{funcdesc}{getcheckinterval}{}
|
||||||
Return the interpreter's ``check interval'';
|
Return the interpreter's ``check interval'';
|
||||||
see \function{setcheckinterval()}.
|
see \function{setcheckinterval()}.
|
||||||
|
|
|
@ -69,6 +69,10 @@ PyAPI_FUNC(void) PyErr_Print(void);
|
||||||
PyAPI_FUNC(void) PyErr_PrintEx(int);
|
PyAPI_FUNC(void) PyErr_PrintEx(int);
|
||||||
PyAPI_FUNC(void) PyErr_Display(PyObject *, PyObject *, PyObject *);
|
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(int) Py_AtExit(void (*func)(void));
|
||||||
|
|
||||||
PyAPI_FUNC(void) Py_Exit(int);
|
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("StringIO")
|
||||||
self.check_all("UserString")
|
self.check_all("UserString")
|
||||||
self.check_all("aifc")
|
self.check_all("aifc")
|
||||||
self.check_all("atexit")
|
|
||||||
self.check_all("audiodev")
|
self.check_all("audiodev")
|
||||||
self.check_all("base64")
|
self.check_all("base64")
|
||||||
self.check_all("bdb")
|
self.check_all("bdb")
|
||||||
|
|
|
@ -4,97 +4,112 @@ import StringIO
|
||||||
import atexit
|
import atexit
|
||||||
from test import test_support
|
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):
|
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):
|
def test_args(self):
|
||||||
# be sure args are handled properly
|
# be sure args are handled properly
|
||||||
s = StringIO.StringIO()
|
atexit.register(h1)
|
||||||
sys.stdout = sys.stderr = s
|
atexit.register(h4)
|
||||||
save_handlers = atexit._exithandlers
|
atexit.register(h4, 4, kw="abc")
|
||||||
atexit._exithandlers = []
|
atexit._run_exitfuncs()
|
||||||
try:
|
|
||||||
atexit.register(self.h1)
|
self.assertEqual(self.stream.getvalue(),
|
||||||
atexit.register(self.h4)
|
"h4 (4,) {'kw': 'abc'}\nh4 () {}\nh1\n")
|
||||||
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")
|
|
||||||
|
|
||||||
def test_order(self):
|
def test_order(self):
|
||||||
# be sure handlers are executed in reverse order
|
# be sure handlers are executed in reverse order
|
||||||
s = StringIO.StringIO()
|
atexit.register(h1)
|
||||||
sys.stdout = sys.stderr = s
|
atexit.register(h2)
|
||||||
save_handlers = atexit._exithandlers
|
atexit.register(h3)
|
||||||
atexit._exithandlers = []
|
atexit._run_exitfuncs()
|
||||||
try:
|
|
||||||
atexit.register(self.h1)
|
self.assertEqual(self.stream.getvalue(), "h3\nh2\nh1\n")
|
||||||
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")
|
|
||||||
|
|
||||||
def test_raise(self):
|
def test_raise(self):
|
||||||
# be sure raises are handled properly
|
# be sure raises are handled properly
|
||||||
s = StringIO.StringIO()
|
atexit.register(raise1)
|
||||||
sys.stdout = sys.stderr = s
|
atexit.register(raise2)
|
||||||
save_handlers = atexit._exithandlers
|
|
||||||
atexit._exithandlers = []
|
self.assertRaises(TypeError, atexit._run_exitfuncs)
|
||||||
try:
|
|
||||||
atexit.register(self.raise1)
|
def test_stress(self):
|
||||||
atexit.register(self.raise2)
|
a = [0]
|
||||||
self.assertRaises(TypeError, atexit._run_exitfuncs)
|
def inc():
|
||||||
finally:
|
a[0] += 1
|
||||||
sys.stdout = sys.__stdout__
|
|
||||||
sys.stderr = sys.__stderr__
|
for i in range(128):
|
||||||
atexit._exithandlers = save_handlers
|
atexit.register(inc)
|
||||||
|
atexit._run_exitfuncs()
|
||||||
### helpers
|
|
||||||
def h1(self):
|
self.assertEqual(a[0], 128)
|
||||||
print("h1")
|
|
||||||
|
def test_clear(self):
|
||||||
def h2(self):
|
a = [0]
|
||||||
print("h2")
|
def inc():
|
||||||
|
a[0] += 1
|
||||||
def h3(self):
|
|
||||||
print("h3")
|
atexit.register(inc)
|
||||||
|
atexit._clear()
|
||||||
def h4(self, *args, **kwargs):
|
atexit._run_exitfuncs()
|
||||||
print("h4", args, kwargs)
|
|
||||||
|
self.assertEqual(a[0], 0)
|
||||||
def raise1(self):
|
|
||||||
raise TypeError
|
def test_unregister(self):
|
||||||
|
a = [0]
|
||||||
def raise2(self):
|
def inc():
|
||||||
raise SystemError
|
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():
|
def test_main():
|
||||||
test_support.run_unittest(TestCase)
|
test_support.run_unittest(TestCase)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -28,6 +28,9 @@ TO DO
|
||||||
Core and Builtins
|
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
|
- 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
|
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
|
a parameter list. Also, the metaclass can implement a __prepare__ function
|
||||||
|
@ -156,6 +159,8 @@ Extension Modules
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Patch #1680961: atexit has been reimplemented in C.
|
||||||
|
|
||||||
- Removed all traces of the sets module.
|
- Removed all traces of the sets module.
|
||||||
|
|
||||||
Build
|
Build
|
||||||
|
|
|
@ -176,6 +176,7 @@ GLHACK=-Dclear=__GLclear
|
||||||
#collections collectionsmodule.c # Container types
|
#collections collectionsmodule.c # Container types
|
||||||
#itertools itertoolsmodule.c # Functions creating iterators for efficient looping
|
#itertools itertoolsmodule.c # Functions creating iterators for efficient looping
|
||||||
#strop stropmodule.c # String manipulations
|
#strop stropmodule.c # String manipulations
|
||||||
|
#atexit atexitmodule.c # Register functions to be run at interpreter-shutdown
|
||||||
|
|
||||||
#unicodedata unicodedata.c # static Unicode character database
|
#unicodedata unicodedata.c # static Unicode character database
|
||||||
|
|
||||||
|
|
217
Modules/atexitmodule.c
Normal file
217
Modules/atexitmodule.c
Normal file
|
@ -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 */
|
/* List of names to clear in sys */
|
||||||
static char* sys_deletes[] = {
|
static char* sys_deletes[] = {
|
||||||
"path", "argv", "ps1", "ps2", "exitfunc",
|
"path", "argv", "ps1", "ps2",
|
||||||
"exc_type", "exc_value", "exc_traceback",
|
"exc_type", "exc_value", "exc_traceback",
|
||||||
"last_type", "last_value", "last_traceback",
|
"last_type", "last_value", "last_traceback",
|
||||||
"path_hooks", "path_importer_cache", "meta_path",
|
"path_hooks", "path_importer_cache", "meta_path",
|
||||||
|
|
|
@ -56,7 +56,7 @@ static PyObject *run_pyc_file(FILE *, const char *, PyObject *, PyObject *,
|
||||||
PyCompilerFlags *);
|
PyCompilerFlags *);
|
||||||
static void err_input(perrdetail *);
|
static void err_input(perrdetail *);
|
||||||
static void initsigs(void);
|
static void initsigs(void);
|
||||||
static void call_sys_exitfunc(void);
|
static void call_py_exitfuncs(void);
|
||||||
static void call_ll_exitfuncs(void);
|
static void call_ll_exitfuncs(void);
|
||||||
extern void _PyUnicode_Init(void);
|
extern void _PyUnicode_Init(void);
|
||||||
extern void _PyUnicode_Fini(void);
|
extern void _PyUnicode_Fini(void);
|
||||||
|
@ -355,7 +355,7 @@ Py_Finalize(void)
|
||||||
* threads created thru it, so this also protects pending imports in
|
* threads created thru it, so this also protects pending imports in
|
||||||
* the threads created via Threading.
|
* the threads created via Threading.
|
||||||
*/
|
*/
|
||||||
call_sys_exitfunc();
|
call_py_exitfuncs();
|
||||||
initialized = 0;
|
initialized = 0;
|
||||||
|
|
||||||
/* Get current thread state and interpreter pointer */
|
/* Get current thread state and interpreter pointer */
|
||||||
|
@ -1557,6 +1557,23 @@ Py_FatalError(const char *msg)
|
||||||
#include "pythread.h"
|
#include "pythread.h"
|
||||||
#endif
|
#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
|
#define NEXITFUNCS 32
|
||||||
static void (*exitfuncs[NEXITFUNCS])(void);
|
static void (*exitfuncs[NEXITFUNCS])(void);
|
||||||
static int nexitfuncs = 0;
|
static int nexitfuncs = 0;
|
||||||
|
@ -1569,27 +1586,6 @@ int Py_AtExit(void (*func)(void))
|
||||||
return 0;
|
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
|
static void
|
||||||
call_ll_exitfuncs(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\
|
To customize printing in an interactive session or to install a custom\n\
|
||||||
top-level exception handler, assign other functions to replace these.\n\
|
top-level exception handler, assign other functions to replace these.\n\
|
||||||
\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\
|
stdin -- standard input file object; used by raw_input() and input()\n\
|
||||||
stdout -- standard output file object; used by print()\n\
|
stdout -- standard output file object; used by print()\n\
|
||||||
stderr -- standard error object; used for error messages\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']) )
|
exts.append( Extension('operator', ['operator.c']) )
|
||||||
# _functools
|
# _functools
|
||||||
exts.append( Extension("_functools", ["_functoolsmodule.c"]) )
|
exts.append( Extension("_functools", ["_functoolsmodule.c"]) )
|
||||||
|
# atexit
|
||||||
|
exts.append( Extension("atexit", ["atexitmodule.c"]) )
|
||||||
# Python C API test module
|
# Python C API test module
|
||||||
exts.append( Extension('_testcapi', ['_testcapimodule.c']) )
|
exts.append( Extension('_testcapi', ['_testcapimodule.c']) )
|
||||||
# profilers (_lsprof is for cProfile.py)
|
# profilers (_lsprof is for cProfile.py)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue