summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Snow <ericsnowcurrently@gmail.com>2019-05-10 17:29:55 (GMT)
committerGitHub <noreply@github.com>2019-05-10 17:29:55 (GMT)
commit86ea58149c3e83f402cecd17e6a536865fb06ce1 (patch)
treea7a26190fcb98d298cc992da4ad9d8f663304bdb
parent351c67416ba4451eb3928fa0b2e933c2f25df1a3 (diff)
downloadcpython-86ea58149c3e83f402cecd17e6a536865fb06ce1.zip
cpython-86ea58149c3e83f402cecd17e6a536865fb06ce1.tar.gz
cpython-86ea58149c3e83f402cecd17e6a536865fb06ce1.tar.bz2
bpo-36737: Use the module state C-API for warnings. (gh-13159)
-rw-r--r--Include/internal/pycore_pylifecycle.h2
-rw-r--r--Include/internal/pycore_pystate.h3
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2019-05-07-12-18-11.bpo-36737.XAo6LY.rst2
-rw-r--r--Python/_warnings.c317
-rw-r--r--Python/pylifecycle.c2
-rw-r--r--Python/pystate.c4
6 files changed, 208 insertions, 122 deletions
diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h
index adb1f5d..7144bbc 100644
--- a/Include/internal/pycore_pylifecycle.h
+++ b/Include/internal/pycore_pylifecycle.h
@@ -81,7 +81,7 @@ extern void PyLong_Fini(void);
extern void _PyFaulthandler_Fini(void);
extern void _PyHash_Fini(void);
extern int _PyTraceMalloc_Fini(void);
-extern void _PyWarnings_Fini(_PyRuntimeState *runtime);
+extern void _PyWarnings_Fini(PyInterpreterState *interp);
extern void _PyGILState_Init(
_PyRuntimeState *runtime,
diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h
index 67bcd14..69ceecb 100644
--- a/Include/internal/pycore_pystate.h
+++ b/Include/internal/pycore_pystate.h
@@ -90,6 +90,8 @@ struct _is {
PyObject *pyexitmodule;
uint64_t tstate_next_unique_id;
+
+ struct _warnings_runtime_state warnings;
};
PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T);
@@ -179,7 +181,6 @@ typedef struct pyruntimestate {
int nexitfuncs;
struct _gc_runtime_state gc;
- struct _warnings_runtime_state warnings;
struct _ceval_runtime_state ceval;
struct _gilstate_runtime_state gilstate;
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-07-12-18-11.bpo-36737.XAo6LY.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-07-12-18-11.bpo-36737.XAo6LY.rst
new file mode 100644
index 0000000..7a2c647
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-07-12-18-11.bpo-36737.XAo6LY.rst
@@ -0,0 +1,2 @@
+Move PyRuntimeState.warnings into per-interpreter state (via "module
+state").
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 388b299..0b19258 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -15,6 +15,134 @@ _Py_IDENTIFIER(default);
_Py_IDENTIFIER(ignore);
#endif
+
+/*************************************************************************/
+
+typedef struct _warnings_runtime_state WarningsState;
+
+/* Forward declaration of the _warnings module definition. */
+static struct PyModuleDef warningsmodule;
+
+/* Given a module object, get its per-module state. */
+static WarningsState *
+_Warnings_GetState()
+{
+ PyThreadState *tstate = PyThreadState_GET();
+ if (tstate == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "_Warnings_GetState: could not identify current interpreter");
+ return NULL;
+ }
+ return &tstate->interp->warnings;
+}
+
+/* Clear the given warnings module state. */
+static void
+_Warnings_ClearState(WarningsState *st)
+{
+ Py_CLEAR(st->filters);
+ Py_CLEAR(st->once_registry);
+ Py_CLEAR(st->default_action);
+}
+
+#ifndef Py_DEBUG
+static PyObject *
+create_filter(PyObject *category, _Py_Identifier *id, const char *modname)
+{
+ PyObject *modname_obj = NULL;
+ PyObject *action_str = _PyUnicode_FromId(id);
+ if (action_str == NULL) {
+ return NULL;
+ }
+
+ /* Default to "no module name" for initial filter set */
+ if (modname != NULL) {
+ modname_obj = PyUnicode_InternFromString(modname);
+ if (modname_obj == NULL) {
+ return NULL;
+ }
+ } else {
+ modname_obj = Py_None;
+ }
+
+ /* This assumes the line number is zero for now. */
+ return PyTuple_Pack(5, action_str, Py_None,
+ category, modname_obj, _PyLong_Zero);
+}
+#endif
+
+static PyObject *
+init_filters(void)
+{
+#ifdef Py_DEBUG
+ /* Py_DEBUG builds show all warnings by default */
+ return PyList_New(0);
+#else
+ /* Other builds ignore a number of warning categories by default */
+ PyObject *filters = PyList_New(5);
+ if (filters == NULL) {
+ return NULL;
+ }
+
+ size_t pos = 0; /* Post-incremented in each use. */
+ PyList_SET_ITEM(filters, pos++,
+ create_filter(PyExc_DeprecationWarning, &PyId_default, "__main__"));
+ PyList_SET_ITEM(filters, pos++,
+ create_filter(PyExc_DeprecationWarning, &PyId_ignore, NULL));
+ PyList_SET_ITEM(filters, pos++,
+ create_filter(PyExc_PendingDeprecationWarning, &PyId_ignore, NULL));
+ PyList_SET_ITEM(filters, pos++,
+ create_filter(PyExc_ImportWarning, &PyId_ignore, NULL));
+ PyList_SET_ITEM(filters, pos++,
+ create_filter(PyExc_ResourceWarning, &PyId_ignore, NULL));
+
+ for (size_t x = 0; x < pos; x++) {
+ if (PyList_GET_ITEM(filters, x) == NULL) {
+ Py_DECREF(filters);
+ return NULL;
+ }
+ }
+ return filters;
+#endif
+}
+
+/* Initialize the given warnings module state. */
+static int
+_Warnings_InitState(WarningsState *st)
+{
+ if (st->filters == NULL) {
+ st->filters = init_filters();
+ if (st->filters == NULL) {
+ goto error;
+ }
+ }
+
+ if (st->once_registry == NULL) {
+ st->once_registry = PyDict_New();
+ if (st->once_registry == NULL) {
+ goto error;
+ }
+ }
+
+ if (st->default_action == NULL) {
+ st->default_action = PyUnicode_FromString("default");
+ if (st->default_action == NULL) {
+ goto error;
+ }
+ }
+
+ st->filters_version = 0;
+
+ return 0;
+
+error:
+ _Warnings_ClearState(st);
+ return -1;
+}
+
+
+/*************************************************************************/
+
static int
check_matched(PyObject *obj, PyObject *arg)
{
@@ -93,7 +221,7 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import)
static PyObject *
-get_once_registry(void)
+get_once_registry(WarningsState *st)
{
PyObject *registry;
_Py_IDENTIFIER(onceregistry);
@@ -102,8 +230,8 @@ get_once_registry(void)
if (registry == NULL) {
if (PyErr_Occurred())
return NULL;
- assert(_PyRuntime.warnings.once_registry);
- return _PyRuntime.warnings.once_registry;
+ assert(st->once_registry);
+ return st->once_registry;
}
if (!PyDict_Check(registry)) {
PyErr_Format(PyExc_TypeError,
@@ -113,13 +241,13 @@ get_once_registry(void)
Py_DECREF(registry);
return NULL;
}
- Py_SETREF(_PyRuntime.warnings.once_registry, registry);
+ Py_SETREF(st->once_registry, registry);
return registry;
}
static PyObject *
-get_default_action(void)
+get_default_action(WarningsState *st)
{
PyObject *default_action;
_Py_IDENTIFIER(defaultaction);
@@ -129,8 +257,8 @@ get_default_action(void)
if (PyErr_Occurred()) {
return NULL;
}
- assert(_PyRuntime.warnings.default_action);
- return _PyRuntime.warnings.default_action;
+ assert(st->default_action);
+ return st->default_action;
}
if (!PyUnicode_Check(default_action)) {
PyErr_Format(PyExc_TypeError,
@@ -140,7 +268,7 @@ get_default_action(void)
Py_DECREF(default_action);
return NULL;
}
- Py_SETREF(_PyRuntime.warnings.default_action, default_action);
+ Py_SETREF(st->default_action, default_action);
return default_action;
}
@@ -154,6 +282,10 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
Py_ssize_t i;
PyObject *warnings_filters;
_Py_IDENTIFIER(filters);
+ WarningsState *st = _Warnings_GetState();
+ if (st == NULL) {
+ return NULL;
+ }
warnings_filters = get_warnings_attr(&PyId_filters, 0);
if (warnings_filters == NULL) {
@@ -161,17 +293,17 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
return NULL;
}
else {
- Py_SETREF(_PyRuntime.warnings.filters, warnings_filters);
+ Py_SETREF(st->filters, warnings_filters);
}
- PyObject *filters = _PyRuntime.warnings.filters;
+ PyObject *filters = st->filters;
if (filters == NULL || !PyList_Check(filters)) {
PyErr_SetString(PyExc_ValueError,
MODULE_NAME ".filters must be a list");
return NULL;
}
- /* _PyRuntime.warnings.filters could change while we are iterating over it. */
+ /* WarningsState.filters could change while we are iterating over it. */
for (i = 0; i < PyList_GET_SIZE(filters); i++) {
PyObject *tmp_item, *action, *msg, *cat, *mod, *ln_obj;
Py_ssize_t ln;
@@ -232,7 +364,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
Py_DECREF(tmp_item);
}
- action = get_default_action();
+ action = get_default_action(st);
if (action != NULL) {
Py_INCREF(Py_None);
*item = Py_None;
@@ -252,16 +384,20 @@ already_warned(PyObject *registry, PyObject *key, int should_set)
if (key == NULL)
return -1;
+ WarningsState *st = _Warnings_GetState();
+ if (st == NULL) {
+ return -1;
+ }
version_obj = _PyDict_GetItemIdWithError(registry, &PyId_version);
if (version_obj == NULL
|| !PyLong_CheckExact(version_obj)
- || PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version)
+ || PyLong_AsLong(version_obj) != st->filters_version)
{
if (PyErr_Occurred()) {
return -1;
}
PyDict_Clear(registry);
- version_obj = PyLong_FromLong(_PyRuntime.warnings.filters_version);
+ version_obj = PyLong_FromLong(st->filters_version);
if (version_obj == NULL)
return -1;
if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) {
@@ -567,11 +703,15 @@ warn_explicit(PyObject *category, PyObject *message,
if (_PyUnicode_EqualToASCIIString(action, "once")) {
if (registry == NULL || registry == Py_None) {
- registry = get_once_registry();
+ WarningsState *st = _Warnings_GetState();
+ if (st == NULL) {
+ goto cleanup;
+ }
+ registry = get_once_registry(st);
if (registry == NULL)
goto cleanup;
}
- /* _PyRuntime.warnings.once_registry[(text, category)] = 1 */
+ /* WarningsState.once_registry[(text, category)] = 1 */
rc = update_registry(registry, text, category, 0);
}
else if (_PyUnicode_EqualToASCIIString(action, "module")) {
@@ -925,7 +1065,11 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
static PyObject *
warnings_filters_mutated(PyObject *self, PyObject *args)
{
- _PyRuntime.warnings.filters_version++;
+ WarningsState *st = _Warnings_GetState();
+ if (st == NULL) {
+ return NULL;
+ }
+ st->filters_version++;
Py_RETURN_NONE;
}
@@ -1175,78 +1319,16 @@ static PyMethodDef warnings_functions[] = {
};
-#ifndef Py_DEBUG
-static PyObject *
-create_filter(PyObject *category, _Py_Identifier *id, const char *modname)
-{
- PyObject *modname_obj = NULL;
- PyObject *action_str = _PyUnicode_FromId(id);
- if (action_str == NULL) {
- return NULL;
- }
-
- /* Default to "no module name" for initial filter set */
- if (modname != NULL) {
- modname_obj = PyUnicode_InternFromString(modname);
- if (modname_obj == NULL) {
- return NULL;
- }
- } else {
- modname_obj = Py_None;
- }
-
- /* This assumes the line number is zero for now. */
- return PyTuple_Pack(5, action_str, Py_None,
- category, modname_obj, _PyLong_Zero);
-}
-#endif
-
-
-static PyObject *
-init_filters(void)
-{
-#ifdef Py_DEBUG
- /* Py_DEBUG builds show all warnings by default */
- return PyList_New(0);
-#else
- /* Other builds ignore a number of warning categories by default */
- PyObject *filters = PyList_New(5);
- if (filters == NULL) {
- return NULL;
- }
-
- size_t pos = 0; /* Post-incremented in each use. */
- PyList_SET_ITEM(filters, pos++,
- create_filter(PyExc_DeprecationWarning, &PyId_default, "__main__"));
- PyList_SET_ITEM(filters, pos++,
- create_filter(PyExc_DeprecationWarning, &PyId_ignore, NULL));
- PyList_SET_ITEM(filters, pos++,
- create_filter(PyExc_PendingDeprecationWarning, &PyId_ignore, NULL));
- PyList_SET_ITEM(filters, pos++,
- create_filter(PyExc_ImportWarning, &PyId_ignore, NULL));
- PyList_SET_ITEM(filters, pos++,
- create_filter(PyExc_ResourceWarning, &PyId_ignore, NULL));
-
- for (size_t x = 0; x < pos; x++) {
- if (PyList_GET_ITEM(filters, x) == NULL) {
- Py_DECREF(filters);
- return NULL;
- }
- }
- return filters;
-#endif
-}
-
static struct PyModuleDef warningsmodule = {
PyModuleDef_HEAD_INIT,
- MODULE_NAME,
- warnings__doc__,
- 0,
- warnings_functions,
- NULL,
- NULL,
- NULL,
- NULL
+ MODULE_NAME, /* m_name */
+ warnings__doc__, /* m_doc */
+ 0, /* m_size */
+ warnings_functions, /* m_methods */
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
};
@@ -1256,49 +1338,46 @@ _PyWarnings_Init(void)
PyObject *m;
m = PyModule_Create(&warningsmodule);
- if (m == NULL)
+ if (m == NULL) {
return NULL;
+ }
- struct _warnings_runtime_state *state = &_PyRuntime.warnings;
- if (state->filters == NULL) {
- state->filters = init_filters();
- if (state->filters == NULL)
- return NULL;
+ WarningsState *st = _Warnings_GetState();
+ if (st == NULL) {
+ goto error;
+ }
+ if (_Warnings_InitState(st) < 0) {
+ goto error;
}
- Py_INCREF(state->filters);
- if (PyModule_AddObject(m, "filters", state->filters) < 0)
- return NULL;
- if (state->once_registry == NULL) {
- state->once_registry = PyDict_New();
- if (state->once_registry == NULL)
- return NULL;
+ Py_INCREF(st->filters);
+ if (PyModule_AddObject(m, "filters", st->filters) < 0) {
+ goto error;
}
- Py_INCREF(state->once_registry);
- if (PyModule_AddObject(m, "_onceregistry",
- state->once_registry) < 0)
- return NULL;
- if (state->default_action == NULL) {
- state->default_action = PyUnicode_FromString("default");
- if (state->default_action == NULL)
- return NULL;
+ Py_INCREF(st->once_registry);
+ if (PyModule_AddObject(m, "_onceregistry", st->once_registry) < 0) {
+ goto error;
+ }
+
+ Py_INCREF(st->default_action);
+ if (PyModule_AddObject(m, "_defaultaction", st->default_action) < 0) {
+ goto error;
}
- Py_INCREF(state->default_action);
- if (PyModule_AddObject(m, "_defaultaction",
- state->default_action) < 0)
- return NULL;
- state->filters_version = 0;
return m;
-}
+error:
+ if (st != NULL) {
+ _Warnings_ClearState(st);
+ }
+ Py_DECREF(m);
+ return NULL;
+}
+// We need this to ensure that warnings still work until late in finalization.
void
-_PyWarnings_Fini(_PyRuntimeState *runtime)
+_PyWarnings_Fini(PyInterpreterState *interp)
{
- struct _warnings_runtime_state *state = &runtime->warnings;
- Py_CLEAR(state->filters);
- Py_CLEAR(state->once_registry);
- Py_CLEAR(state->default_action);
+ _Warnings_ClearState(&interp->warnings);
}
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index bd4d1d9..32902aa 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1288,7 +1288,7 @@ Py_FinalizeEx(void)
PyDict_Fini();
PySlice_Fini();
_PyGC_Fini(runtime);
- _PyWarnings_Fini(runtime);
+ _PyWarnings_Fini(interp);
_Py_HashRandomization_Fini();
_PyArg_Fini();
PyAsyncGen_Fini();
diff --git a/Python/pystate.c b/Python/pystate.c
index e9c4c7d..44acfed 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -5,6 +5,7 @@
#include "pycore_coreconfig.h"
#include "pycore_pymem.h"
#include "pycore_pystate.h"
+#include "pycore_pylifecycle.h"
/* --------------------------------------------------------------------------
CAUTION
@@ -257,6 +258,9 @@ _PyInterpreterState_Clear(_PyRuntimeState *runtime, PyInterpreterState *interp)
Py_CLEAR(interp->after_forkers_parent);
Py_CLEAR(interp->after_forkers_child);
#endif
+ if (runtime->finalizing == NULL) {
+ _PyWarnings_Fini(interp);
+ }
// XXX Once we have one allocator per interpreter (i.e.
// per-interpreter GC) we must ensure that all of the interpreter's
// objects have been cleaned up at the point.