Commit 86ea5814 authored by Eric Snow's avatar Eric Snow Committed by GitHub

bpo-36737: Use the module state C-API for warnings. (gh-13159)

parent 351c6741
...@@ -81,7 +81,7 @@ extern void PyLong_Fini(void); ...@@ -81,7 +81,7 @@ extern void PyLong_Fini(void);
extern void _PyFaulthandler_Fini(void); extern void _PyFaulthandler_Fini(void);
extern void _PyHash_Fini(void); extern void _PyHash_Fini(void);
extern int _PyTraceMalloc_Fini(void); extern int _PyTraceMalloc_Fini(void);
extern void _PyWarnings_Fini(_PyRuntimeState *runtime); extern void _PyWarnings_Fini(PyInterpreterState *interp);
extern void _PyGILState_Init( extern void _PyGILState_Init(
_PyRuntimeState *runtime, _PyRuntimeState *runtime,
......
...@@ -90,6 +90,8 @@ struct _is { ...@@ -90,6 +90,8 @@ struct _is {
PyObject *pyexitmodule; PyObject *pyexitmodule;
uint64_t tstate_next_unique_id; uint64_t tstate_next_unique_id;
struct _warnings_runtime_state warnings;
}; };
PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T); PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T);
...@@ -179,7 +181,6 @@ typedef struct pyruntimestate { ...@@ -179,7 +181,6 @@ typedef struct pyruntimestate {
int nexitfuncs; int nexitfuncs;
struct _gc_runtime_state gc; struct _gc_runtime_state gc;
struct _warnings_runtime_state warnings;
struct _ceval_runtime_state ceval; struct _ceval_runtime_state ceval;
struct _gilstate_runtime_state gilstate; struct _gilstate_runtime_state gilstate;
......
Move PyRuntimeState.warnings into per-interpreter state (via "module
state").
...@@ -15,6 +15,134 @@ _Py_IDENTIFIER(default); ...@@ -15,6 +15,134 @@ _Py_IDENTIFIER(default);
_Py_IDENTIFIER(ignore); _Py_IDENTIFIER(ignore);
#endif #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 static int
check_matched(PyObject *obj, PyObject *arg) check_matched(PyObject *obj, PyObject *arg)
{ {
...@@ -93,7 +221,7 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import) ...@@ -93,7 +221,7 @@ get_warnings_attr(_Py_Identifier *attr_id, int try_import)
static PyObject * static PyObject *
get_once_registry(void) get_once_registry(WarningsState *st)
{ {
PyObject *registry; PyObject *registry;
_Py_IDENTIFIER(onceregistry); _Py_IDENTIFIER(onceregistry);
...@@ -102,8 +230,8 @@ get_once_registry(void) ...@@ -102,8 +230,8 @@ get_once_registry(void)
if (registry == NULL) { if (registry == NULL) {
if (PyErr_Occurred()) if (PyErr_Occurred())
return NULL; return NULL;
assert(_PyRuntime.warnings.once_registry); assert(st->once_registry);
return _PyRuntime.warnings.once_registry; return st->once_registry;
} }
if (!PyDict_Check(registry)) { if (!PyDict_Check(registry)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
...@@ -113,13 +241,13 @@ get_once_registry(void) ...@@ -113,13 +241,13 @@ get_once_registry(void)
Py_DECREF(registry); Py_DECREF(registry);
return NULL; return NULL;
} }
Py_SETREF(_PyRuntime.warnings.once_registry, registry); Py_SETREF(st->once_registry, registry);
return registry; return registry;
} }
static PyObject * static PyObject *
get_default_action(void) get_default_action(WarningsState *st)
{ {
PyObject *default_action; PyObject *default_action;
_Py_IDENTIFIER(defaultaction); _Py_IDENTIFIER(defaultaction);
...@@ -129,8 +257,8 @@ get_default_action(void) ...@@ -129,8 +257,8 @@ get_default_action(void)
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
return NULL; return NULL;
} }
assert(_PyRuntime.warnings.default_action); assert(st->default_action);
return _PyRuntime.warnings.default_action; return st->default_action;
} }
if (!PyUnicode_Check(default_action)) { if (!PyUnicode_Check(default_action)) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
...@@ -140,7 +268,7 @@ get_default_action(void) ...@@ -140,7 +268,7 @@ get_default_action(void)
Py_DECREF(default_action); Py_DECREF(default_action);
return NULL; return NULL;
} }
Py_SETREF(_PyRuntime.warnings.default_action, default_action); Py_SETREF(st->default_action, default_action);
return default_action; return default_action;
} }
...@@ -154,6 +282,10 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno, ...@@ -154,6 +282,10 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
Py_ssize_t i; Py_ssize_t i;
PyObject *warnings_filters; PyObject *warnings_filters;
_Py_IDENTIFIER(filters); _Py_IDENTIFIER(filters);
WarningsState *st = _Warnings_GetState();
if (st == NULL) {
return NULL;
}
warnings_filters = get_warnings_attr(&PyId_filters, 0); warnings_filters = get_warnings_attr(&PyId_filters, 0);
if (warnings_filters == NULL) { if (warnings_filters == NULL) {
...@@ -161,17 +293,17 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno, ...@@ -161,17 +293,17 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
return NULL; return NULL;
} }
else { 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)) { if (filters == NULL || !PyList_Check(filters)) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
MODULE_NAME ".filters must be a list"); MODULE_NAME ".filters must be a list");
return NULL; 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++) { for (i = 0; i < PyList_GET_SIZE(filters); i++) {
PyObject *tmp_item, *action, *msg, *cat, *mod, *ln_obj; PyObject *tmp_item, *action, *msg, *cat, *mod, *ln_obj;
Py_ssize_t ln; Py_ssize_t ln;
...@@ -232,7 +364,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno, ...@@ -232,7 +364,7 @@ get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
Py_DECREF(tmp_item); Py_DECREF(tmp_item);
} }
action = get_default_action(); action = get_default_action(st);
if (action != NULL) { if (action != NULL) {
Py_INCREF(Py_None); Py_INCREF(Py_None);
*item = Py_None; *item = Py_None;
...@@ -252,16 +384,20 @@ already_warned(PyObject *registry, PyObject *key, int should_set) ...@@ -252,16 +384,20 @@ already_warned(PyObject *registry, PyObject *key, int should_set)
if (key == NULL) if (key == NULL)
return -1; return -1;
WarningsState *st = _Warnings_GetState();
if (st == NULL) {
return -1;
}
version_obj = _PyDict_GetItemIdWithError(registry, &PyId_version); version_obj = _PyDict_GetItemIdWithError(registry, &PyId_version);
if (version_obj == NULL if (version_obj == NULL
|| !PyLong_CheckExact(version_obj) || !PyLong_CheckExact(version_obj)
|| PyLong_AsLong(version_obj) != _PyRuntime.warnings.filters_version) || PyLong_AsLong(version_obj) != st->filters_version)
{ {
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
return -1; return -1;
} }
PyDict_Clear(registry); PyDict_Clear(registry);
version_obj = PyLong_FromLong(_PyRuntime.warnings.filters_version); version_obj = PyLong_FromLong(st->filters_version);
if (version_obj == NULL) if (version_obj == NULL)
return -1; return -1;
if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) { if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) {
...@@ -567,11 +703,15 @@ warn_explicit(PyObject *category, PyObject *message, ...@@ -567,11 +703,15 @@ warn_explicit(PyObject *category, PyObject *message,
if (_PyUnicode_EqualToASCIIString(action, "once")) { if (_PyUnicode_EqualToASCIIString(action, "once")) {
if (registry == NULL || registry == Py_None) { 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) if (registry == NULL)
goto cleanup; goto cleanup;
} }
/* _PyRuntime.warnings.once_registry[(text, category)] = 1 */ /* WarningsState.once_registry[(text, category)] = 1 */
rc = update_registry(registry, text, category, 0); rc = update_registry(registry, text, category, 0);
} }
else if (_PyUnicode_EqualToASCIIString(action, "module")) { else if (_PyUnicode_EqualToASCIIString(action, "module")) {
...@@ -925,7 +1065,11 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds) ...@@ -925,7 +1065,11 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
static PyObject * static PyObject *
warnings_filters_mutated(PyObject *self, PyObject *args) 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; Py_RETURN_NONE;
} }
...@@ -1175,78 +1319,16 @@ static PyMethodDef warnings_functions[] = { ...@@ -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 = { static struct PyModuleDef warningsmodule = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
MODULE_NAME, MODULE_NAME, /* m_name */
warnings__doc__, warnings__doc__, /* m_doc */
0, 0, /* m_size */
warnings_functions, warnings_functions, /* m_methods */
NULL, NULL, /* m_reload */
NULL, NULL, /* m_traverse */
NULL, NULL, /* m_clear */
NULL NULL /* m_free */
}; };
...@@ -1256,49 +1338,46 @@ _PyWarnings_Init(void) ...@@ -1256,49 +1338,46 @@ _PyWarnings_Init(void)
PyObject *m; PyObject *m;
m = PyModule_Create(&warningsmodule); m = PyModule_Create(&warningsmodule);
if (m == NULL) if (m == NULL) {
return NULL; return NULL;
}
struct _warnings_runtime_state *state = &_PyRuntime.warnings; WarningsState *st = _Warnings_GetState();
if (state->filters == NULL) { if (st == NULL) {
state->filters = init_filters(); goto error;
if (state->filters == NULL) }
return NULL; 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) { Py_INCREF(st->filters);
state->once_registry = PyDict_New(); if (PyModule_AddObject(m, "filters", st->filters) < 0) {
if (state->once_registry == NULL) goto error;
return NULL;
} }
Py_INCREF(state->once_registry);
if (PyModule_AddObject(m, "_onceregistry",
state->once_registry) < 0)
return NULL;
if (state->default_action == NULL) { Py_INCREF(st->once_registry);
state->default_action = PyUnicode_FromString("default"); if (PyModule_AddObject(m, "_onceregistry", st->once_registry) < 0) {
if (state->default_action == NULL) goto error;
return NULL; }
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; 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 void
_PyWarnings_Fini(_PyRuntimeState *runtime) _PyWarnings_Fini(PyInterpreterState *interp)
{ {
struct _warnings_runtime_state *state = &runtime->warnings; _Warnings_ClearState(&interp->warnings);
Py_CLEAR(state->filters);
Py_CLEAR(state->once_registry);
Py_CLEAR(state->default_action);
} }
...@@ -1288,7 +1288,7 @@ Py_FinalizeEx(void) ...@@ -1288,7 +1288,7 @@ Py_FinalizeEx(void)
PyDict_Fini(); PyDict_Fini();
PySlice_Fini(); PySlice_Fini();
_PyGC_Fini(runtime); _PyGC_Fini(runtime);
_PyWarnings_Fini(runtime); _PyWarnings_Fini(interp);
_Py_HashRandomization_Fini(); _Py_HashRandomization_Fini();
_PyArg_Fini(); _PyArg_Fini();
PyAsyncGen_Fini(); PyAsyncGen_Fini();
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "pycore_coreconfig.h" #include "pycore_coreconfig.h"
#include "pycore_pymem.h" #include "pycore_pymem.h"
#include "pycore_pystate.h" #include "pycore_pystate.h"
#include "pycore_pylifecycle.h"
/* -------------------------------------------------------------------------- /* --------------------------------------------------------------------------
CAUTION CAUTION
...@@ -257,6 +258,9 @@ _PyInterpreterState_Clear(_PyRuntimeState *runtime, PyInterpreterState *interp) ...@@ -257,6 +258,9 @@ _PyInterpreterState_Clear(_PyRuntimeState *runtime, PyInterpreterState *interp)
Py_CLEAR(interp->after_forkers_parent); Py_CLEAR(interp->after_forkers_parent);
Py_CLEAR(interp->after_forkers_child); Py_CLEAR(interp->after_forkers_child);
#endif #endif
if (runtime->finalizing == NULL) {
_PyWarnings_Fini(interp);
}
// XXX Once we have one allocator per interpreter (i.e. // XXX Once we have one allocator per interpreter (i.e.
// per-interpreter GC) we must ensure that all of the interpreter's // per-interpreter GC) we must ensure that all of the interpreter's
// objects have been cleaned up at the point. // objects have been cleaned up at the point.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment