aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Python/_warnings.c
diff options
context:
space:
mode:
authornkozlovskiy <nmk@ydb.tech>2023-09-29 12:24:06 +0300
committernkozlovskiy <nmk@ydb.tech>2023-09-29 12:41:34 +0300
commite0e3e1717e3d33762ce61950504f9637a6e669ed (patch)
treebca3ff6939b10ed60c3d5c12439963a1146b9711 /contrib/tools/python3/src/Python/_warnings.c
parent38f2c5852db84c7b4d83adfcb009eb61541d1ccd (diff)
downloadydb-e0e3e1717e3d33762ce61950504f9637a6e669ed.tar.gz
add ydb deps
Diffstat (limited to 'contrib/tools/python3/src/Python/_warnings.c')
-rw-r--r--contrib/tools/python3/src/Python/_warnings.c1419
1 files changed, 1419 insertions, 0 deletions
diff --git a/contrib/tools/python3/src/Python/_warnings.c b/contrib/tools/python3/src/Python/_warnings.c
new file mode 100644
index 0000000000..2815f7987a
--- /dev/null
+++ b/contrib/tools/python3/src/Python/_warnings.c
@@ -0,0 +1,1419 @@
+#include "Python.h"
+#include "pycore_initconfig.h"
+#include "pycore_interp.h" // PyInterpreterState.warnings
+#include "pycore_long.h" // _PyLong_GetZero()
+#include "pycore_pyerrors.h"
+#include "pycore_pystate.h" // _PyThreadState_GET()
+#include "pycore_frame.h"
+#include "clinic/_warnings.c.h"
+
+#define MODULE_NAME "_warnings"
+
+PyDoc_STRVAR(warnings__doc__,
+MODULE_NAME " provides basic warning filtering support.\n"
+"It is a helper module to speed up interpreter start-up.");
+
+
+/*************************************************************************/
+
+typedef struct _warnings_runtime_state WarningsState;
+
+static inline int
+check_interp(PyInterpreterState *interp)
+{
+ if (interp == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "warnings_get_state: could not identify "
+ "current interpreter");
+ return 0;
+ }
+ return 1;
+}
+
+static inline PyInterpreterState *
+get_current_interp(void)
+{
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return check_interp(interp) ? interp : NULL;
+}
+
+static inline PyThreadState *
+get_current_tstate(void)
+{
+ PyThreadState *tstate = _PyThreadState_GET();
+ if (tstate == NULL) {
+ (void)check_interp(NULL);
+ return NULL;
+ }
+ return check_interp(tstate->interp) ? tstate : NULL;
+}
+
+/* Given a module object, get its per-module state. */
+static WarningsState *
+warnings_get_state(PyInterpreterState *interp)
+{
+ return &interp->warnings;
+}
+
+/* Clear the given warnings module state. */
+static void
+warnings_clear_state(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, PyObject *action_str, const char *modname)
+{
+ PyObject *modname_obj = 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_NewRef(Py_None);
+ }
+
+ /* This assumes the line number is zero for now. */
+ PyObject *filter = PyTuple_Pack(5, action_str, Py_None,
+ category, modname_obj, _PyLong_GetZero());
+ Py_DECREF(modname_obj);
+ return filter;
+}
+#endif
+
+static PyObject *
+init_filters(PyInterpreterState *interp)
+{
+#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. */
+#define ADD(TYPE, ACTION, MODNAME) \
+ PyList_SET_ITEM(filters, pos++, \
+ create_filter(TYPE, &_Py_ID(ACTION), MODNAME));
+ ADD(PyExc_DeprecationWarning, default, "__main__");
+ ADD(PyExc_DeprecationWarning, ignore, NULL);
+ ADD(PyExc_PendingDeprecationWarning, ignore, NULL);
+ ADD(PyExc_ImportWarning, ignore, NULL);
+ ADD(PyExc_ResourceWarning, ignore, NULL);
+#undef ADD
+
+ 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. */
+int
+_PyWarnings_InitState(PyInterpreterState *interp)
+{
+ WarningsState *st = &interp->warnings;
+
+ if (st->filters == NULL) {
+ st->filters = init_filters(interp);
+ if (st->filters == NULL) {
+ return -1;
+ }
+ }
+
+ if (st->once_registry == NULL) {
+ st->once_registry = PyDict_New();
+ if (st->once_registry == NULL) {
+ return -1;
+ }
+ }
+
+ if (st->default_action == NULL) {
+ st->default_action = PyUnicode_FromString("default");
+ if (st->default_action == NULL) {
+ return -1;
+ }
+ }
+
+ st->filters_version = 0;
+ return 0;
+}
+
+
+/*************************************************************************/
+
+static int
+check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
+{
+ PyObject *result;
+ int rc;
+
+ /* A 'None' filter always matches */
+ if (obj == Py_None)
+ return 1;
+
+ /* An internal plain text default filter must match exactly */
+ if (PyUnicode_CheckExact(obj)) {
+ int cmp_result = PyUnicode_Compare(obj, arg);
+ if (cmp_result == -1 && PyErr_Occurred()) {
+ return -1;
+ }
+ return !cmp_result;
+ }
+
+ /* Otherwise assume a regex filter and call its match() method */
+ result = PyObject_CallMethodOneArg(obj, &_Py_ID(match), arg);
+ if (result == NULL)
+ return -1;
+
+ rc = PyObject_IsTrue(result);
+ Py_DECREF(result);
+ return rc;
+}
+
+#define GET_WARNINGS_ATTR(interp, ATTR, try_import) \
+ get_warnings_attr(interp, &_Py_ID(ATTR), try_import)
+
+/*
+ Returns a new reference.
+ A NULL return value can mean false or an error.
+*/
+static PyObject *
+get_warnings_attr(PyInterpreterState *interp, PyObject *attr, int try_import)
+{
+ PyObject *warnings_module, *obj;
+
+ /* don't try to import after the start of the Python finallization */
+ if (try_import && !_Py_IsFinalizing()) {
+ warnings_module = PyImport_Import(&_Py_ID(warnings));
+ if (warnings_module == NULL) {
+ /* Fallback to the C implementation if we cannot get
+ the Python implementation */
+ if (PyErr_ExceptionMatches(PyExc_ImportError)) {
+ PyErr_Clear();
+ }
+ return NULL;
+ }
+ }
+ else {
+ /* if we're so late into Python finalization that the module dict is
+ gone, then we can't even use PyImport_GetModule without triggering
+ an interpreter abort.
+ */
+ if (!interp->modules) {
+ return NULL;
+ }
+ warnings_module = PyImport_GetModule(&_Py_ID(warnings));
+ if (warnings_module == NULL)
+ return NULL;
+ }
+
+ (void)_PyObject_LookupAttr(warnings_module, attr, &obj);
+ Py_DECREF(warnings_module);
+ return obj;
+}
+
+
+static PyObject *
+get_once_registry(PyInterpreterState *interp)
+{
+ PyObject *registry;
+
+ WarningsState *st = warnings_get_state(interp);
+ if (st == NULL) {
+ return NULL;
+ }
+
+ registry = GET_WARNINGS_ATTR(interp, onceregistry, 0);
+ if (registry == NULL) {
+ if (PyErr_Occurred())
+ return NULL;
+ assert(st->once_registry);
+ return st->once_registry;
+ }
+ if (!PyDict_Check(registry)) {
+ PyErr_Format(PyExc_TypeError,
+ MODULE_NAME ".onceregistry must be a dict, "
+ "not '%.200s'",
+ Py_TYPE(registry)->tp_name);
+ Py_DECREF(registry);
+ return NULL;
+ }
+ Py_SETREF(st->once_registry, registry);
+ return registry;
+}
+
+
+static PyObject *
+get_default_action(PyInterpreterState *interp)
+{
+ PyObject *default_action;
+
+ WarningsState *st = warnings_get_state(interp);
+ if (st == NULL) {
+ return NULL;
+ }
+
+ default_action = GET_WARNINGS_ATTR(interp, defaultaction, 0);
+ if (default_action == NULL) {
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ assert(st->default_action);
+ return st->default_action;
+ }
+ if (!PyUnicode_Check(default_action)) {
+ PyErr_Format(PyExc_TypeError,
+ MODULE_NAME ".defaultaction must be a string, "
+ "not '%.200s'",
+ Py_TYPE(default_action)->tp_name);
+ Py_DECREF(default_action);
+ return NULL;
+ }
+ Py_SETREF(st->default_action, default_action);
+ return default_action;
+}
+
+
+/* The item is a new reference. */
+static PyObject*
+get_filter(PyInterpreterState *interp, PyObject *category,
+ PyObject *text, Py_ssize_t lineno,
+ PyObject *module, PyObject **item)
+{
+ PyObject *action;
+ Py_ssize_t i;
+ PyObject *warnings_filters;
+ WarningsState *st = warnings_get_state(interp);
+ if (st == NULL) {
+ return NULL;
+ }
+
+ warnings_filters = GET_WARNINGS_ATTR(interp, filters, 0);
+ if (warnings_filters == NULL) {
+ if (PyErr_Occurred())
+ return NULL;
+ }
+ else {
+ Py_SETREF(st->filters, 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;
+ }
+
+ /* 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;
+ int is_subclass, good_msg, good_mod;
+
+ tmp_item = PyList_GET_ITEM(filters, i);
+ if (!PyTuple_Check(tmp_item) || PyTuple_GET_SIZE(tmp_item) != 5) {
+ PyErr_Format(PyExc_ValueError,
+ MODULE_NAME ".filters item %zd isn't a 5-tuple", i);
+ return NULL;
+ }
+
+ /* Python code: action, msg, cat, mod, ln = item */
+ Py_INCREF(tmp_item);
+ action = PyTuple_GET_ITEM(tmp_item, 0);
+ msg = PyTuple_GET_ITEM(tmp_item, 1);
+ cat = PyTuple_GET_ITEM(tmp_item, 2);
+ mod = PyTuple_GET_ITEM(tmp_item, 3);
+ ln_obj = PyTuple_GET_ITEM(tmp_item, 4);
+
+ if (!PyUnicode_Check(action)) {
+ PyErr_Format(PyExc_TypeError,
+ "action must be a string, not '%.200s'",
+ Py_TYPE(action)->tp_name);
+ Py_DECREF(tmp_item);
+ return NULL;
+ }
+
+ good_msg = check_matched(interp, msg, text);
+ if (good_msg == -1) {
+ Py_DECREF(tmp_item);
+ return NULL;
+ }
+
+ good_mod = check_matched(interp, mod, module);
+ if (good_mod == -1) {
+ Py_DECREF(tmp_item);
+ return NULL;
+ }
+
+ is_subclass = PyObject_IsSubclass(category, cat);
+ if (is_subclass == -1) {
+ Py_DECREF(tmp_item);
+ return NULL;
+ }
+
+ ln = PyLong_AsSsize_t(ln_obj);
+ if (ln == -1 && PyErr_Occurred()) {
+ Py_DECREF(tmp_item);
+ return NULL;
+ }
+
+ if (good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln)) {
+ *item = tmp_item;
+ return action;
+ }
+
+ Py_DECREF(tmp_item);
+ }
+
+ action = get_default_action(interp);
+ if (action != NULL) {
+ Py_INCREF(Py_None);
+ *item = Py_None;
+ return action;
+ }
+
+ return NULL;
+}
+
+
+static int
+already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key,
+ int should_set)
+{
+ PyObject *version_obj, *already_warned;
+
+ if (key == NULL)
+ return -1;
+
+ WarningsState *st = warnings_get_state(interp);
+ if (st == NULL) {
+ return -1;
+ }
+ version_obj = _PyDict_GetItemWithError(registry, &_Py_ID(version));
+ if (version_obj == NULL
+ || !PyLong_CheckExact(version_obj)
+ || PyLong_AsLong(version_obj) != st->filters_version)
+ {
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+ PyDict_Clear(registry);
+ version_obj = PyLong_FromLong(st->filters_version);
+ if (version_obj == NULL)
+ return -1;
+ if (PyDict_SetItem(registry, &_Py_ID(version), version_obj) < 0) {
+ Py_DECREF(version_obj);
+ return -1;
+ }
+ Py_DECREF(version_obj);
+ }
+ else {
+ already_warned = PyDict_GetItemWithError(registry, key);
+ if (already_warned != NULL) {
+ int rc = PyObject_IsTrue(already_warned);
+ if (rc != 0)
+ return rc;
+ }
+ else if (PyErr_Occurred()) {
+ return -1;
+ }
+ }
+
+ /* This warning wasn't found in the registry, set it. */
+ if (should_set)
+ return PyDict_SetItem(registry, key, Py_True);
+ return 0;
+}
+
+/* New reference. */
+static PyObject *
+normalize_module(PyObject *filename)
+{
+ PyObject *module;
+ int kind;
+ const void *data;
+ Py_ssize_t len;
+
+ len = PyUnicode_GetLength(filename);
+ if (len < 0)
+ return NULL;
+
+ if (len == 0)
+ return PyUnicode_FromString("<unknown>");
+
+ kind = PyUnicode_KIND(filename);
+ data = PyUnicode_DATA(filename);
+
+ /* if filename.endswith(".py"): */
+ if (len >= 3 &&
+ PyUnicode_READ(kind, data, len-3) == '.' &&
+ PyUnicode_READ(kind, data, len-2) == 'p' &&
+ PyUnicode_READ(kind, data, len-1) == 'y')
+ {
+ module = PyUnicode_Substring(filename, 0, len-3);
+ }
+ else {
+ module = filename;
+ Py_INCREF(module);
+ }
+ return module;
+}
+
+static int
+update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text,
+ PyObject *category, int add_zero)
+{
+ PyObject *altkey;
+ int rc;
+
+ if (add_zero)
+ altkey = PyTuple_Pack(3, text, category, _PyLong_GetZero());
+ else
+ altkey = PyTuple_Pack(2, text, category);
+
+ rc = already_warned(interp, registry, altkey, 1);
+ Py_XDECREF(altkey);
+ return rc;
+}
+
+static void
+show_warning(PyThreadState *tstate, PyObject *filename, int lineno,
+ PyObject *text, PyObject *category, PyObject *sourceline)
+{
+ PyObject *f_stderr;
+ PyObject *name;
+ char lineno_str[128];
+
+ PyOS_snprintf(lineno_str, sizeof(lineno_str), ":%d: ", lineno);
+
+ name = PyObject_GetAttr(category, &_Py_ID(__name__));
+ if (name == NULL) {
+ goto error;
+ }
+
+ f_stderr = _PySys_GetAttr(tstate, &_Py_ID(stderr));
+ if (f_stderr == NULL) {
+ fprintf(stderr, "lost sys.stderr\n");
+ goto error;
+ }
+
+ /* Print "filename:lineno: category: text\n" */
+ if (PyFile_WriteObject(filename, f_stderr, Py_PRINT_RAW) < 0)
+ goto error;
+ if (PyFile_WriteString(lineno_str, f_stderr) < 0)
+ goto error;
+ if (PyFile_WriteObject(name, f_stderr, Py_PRINT_RAW) < 0)
+ goto error;
+ if (PyFile_WriteString(": ", f_stderr) < 0)
+ goto error;
+ if (PyFile_WriteObject(text, f_stderr, Py_PRINT_RAW) < 0)
+ goto error;
+ if (PyFile_WriteString("\n", f_stderr) < 0)
+ goto error;
+ Py_CLEAR(name);
+
+ /* Print " source_line\n" */
+ if (sourceline) {
+ int kind;
+ const void *data;
+ Py_ssize_t i, len;
+ Py_UCS4 ch;
+ PyObject *truncated;
+
+ if (PyUnicode_READY(sourceline) < 1)
+ goto error;
+
+ kind = PyUnicode_KIND(sourceline);
+ data = PyUnicode_DATA(sourceline);
+ len = PyUnicode_GET_LENGTH(sourceline);
+ for (i=0; i<len; i++) {
+ ch = PyUnicode_READ(kind, data, i);
+ if (ch != ' ' && ch != '\t' && ch != '\014')
+ break;
+ }
+
+ truncated = PyUnicode_Substring(sourceline, i, len);
+ if (truncated == NULL)
+ goto error;
+
+ PyFile_WriteObject(sourceline, f_stderr, Py_PRINT_RAW);
+ Py_DECREF(truncated);
+ PyFile_WriteString("\n", f_stderr);
+ }
+ else {
+ _Py_DisplaySourceLine(f_stderr, filename, lineno, 2, NULL, NULL);
+ }
+
+error:
+ Py_XDECREF(name);
+ PyErr_Clear();
+}
+
+static int
+call_show_warning(PyThreadState *tstate, PyObject *category,
+ PyObject *text, PyObject *message,
+ PyObject *filename, int lineno, PyObject *lineno_obj,
+ PyObject *sourceline, PyObject *source)
+{
+ PyObject *show_fn, *msg, *res, *warnmsg_cls = NULL;
+ PyInterpreterState *interp = tstate->interp;
+
+ /* If the source parameter is set, try to get the Python implementation.
+ The Python implementation is able to log the traceback where the source
+ was allocated, whereas the C implementation doesn't. */
+ show_fn = GET_WARNINGS_ATTR(interp, _showwarnmsg, source != NULL);
+ if (show_fn == NULL) {
+ if (PyErr_Occurred())
+ return -1;
+ show_warning(tstate, filename, lineno, text, category, sourceline);
+ return 0;
+ }
+
+ if (!PyCallable_Check(show_fn)) {
+ PyErr_SetString(PyExc_TypeError,
+ "warnings._showwarnmsg() must be set to a callable");
+ goto error;
+ }
+
+ warnmsg_cls = GET_WARNINGS_ATTR(interp, WarningMessage, 0);
+ if (warnmsg_cls == NULL) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get warnings.WarningMessage");
+ }
+ goto error;
+ }
+
+ msg = PyObject_CallFunctionObjArgs(warnmsg_cls, message, category,
+ filename, lineno_obj, Py_None, Py_None, source,
+ NULL);
+ Py_DECREF(warnmsg_cls);
+ if (msg == NULL)
+ goto error;
+
+ res = PyObject_CallOneArg(show_fn, msg);
+ Py_DECREF(show_fn);
+ Py_DECREF(msg);
+
+ if (res == NULL)
+ return -1;
+
+ Py_DECREF(res);
+ return 0;
+
+error:
+ Py_XDECREF(show_fn);
+ return -1;
+}
+
+static PyObject *
+warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message,
+ PyObject *filename, int lineno,
+ PyObject *module, PyObject *registry, PyObject *sourceline,
+ PyObject *source)
+{
+ PyObject *key = NULL, *text = NULL, *result = NULL, *lineno_obj = NULL;
+ PyObject *item = NULL;
+ PyObject *action;
+ int rc;
+ PyInterpreterState *interp = tstate->interp;
+
+ /* module can be None if a warning is emitted late during Python shutdown.
+ In this case, the Python warnings module was probably unloaded, filters
+ are no more available to choose as action. It is safer to ignore the
+ warning and do nothing. */
+ if (module == Py_None)
+ Py_RETURN_NONE;
+
+ if (registry && !PyDict_Check(registry) && (registry != Py_None)) {
+ PyErr_SetString(PyExc_TypeError, "'registry' must be a dict or None");
+ return NULL;
+ }
+
+ /* Normalize module. */
+ if (module == NULL) {
+ module = normalize_module(filename);
+ if (module == NULL)
+ return NULL;
+ }
+ else
+ Py_INCREF(module);
+
+ /* Normalize message. */
+ Py_INCREF(message); /* DECREF'ed in cleanup. */
+ rc = PyObject_IsInstance(message, PyExc_Warning);
+ if (rc == -1) {
+ goto cleanup;
+ }
+ if (rc == 1) {
+ text = PyObject_Str(message);
+ if (text == NULL)
+ goto cleanup;
+ category = (PyObject*)Py_TYPE(message);
+ }
+ else {
+ text = message;
+ message = PyObject_CallOneArg(category, message);
+ if (message == NULL)
+ goto cleanup;
+ }
+
+ lineno_obj = PyLong_FromLong(lineno);
+ if (lineno_obj == NULL)
+ goto cleanup;
+
+ if (source == Py_None) {
+ source = NULL;
+ }
+
+ /* Create key. */
+ key = PyTuple_Pack(3, text, category, lineno_obj);
+ if (key == NULL)
+ goto cleanup;
+
+ if ((registry != NULL) && (registry != Py_None)) {
+ rc = already_warned(interp, registry, key, 0);
+ if (rc == -1)
+ goto cleanup;
+ else if (rc == 1)
+ goto return_none;
+ /* Else this warning hasn't been generated before. */
+ }
+
+ action = get_filter(interp, category, text, lineno, module, &item);
+ if (action == NULL)
+ goto cleanup;
+
+ if (_PyUnicode_EqualToASCIIString(action, "error")) {
+ PyErr_SetObject(category, message);
+ goto cleanup;
+ }
+
+ if (_PyUnicode_EqualToASCIIString(action, "ignore")) {
+ goto return_none;
+ }
+
+ /* Store in the registry that we've been here, *except* when the action
+ is "always". */
+ rc = 0;
+ if (!_PyUnicode_EqualToASCIIString(action, "always")) {
+ if (registry != NULL && registry != Py_None &&
+ PyDict_SetItem(registry, key, Py_True) < 0)
+ {
+ goto cleanup;
+ }
+
+ if (_PyUnicode_EqualToASCIIString(action, "once")) {
+ if (registry == NULL || registry == Py_None) {
+ registry = get_once_registry(interp);
+ if (registry == NULL)
+ goto cleanup;
+ }
+ /* WarningsState.once_registry[(text, category)] = 1 */
+ rc = update_registry(interp, registry, text, category, 0);
+ }
+ else if (_PyUnicode_EqualToASCIIString(action, "module")) {
+ /* registry[(text, category, 0)] = 1 */
+ if (registry != NULL && registry != Py_None)
+ rc = update_registry(interp, registry, text, category, 0);
+ }
+ else if (!_PyUnicode_EqualToASCIIString(action, "default")) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Unrecognized action (%R) in warnings.filters:\n %R",
+ action, item);
+ goto cleanup;
+ }
+ }
+
+ if (rc == 1) /* Already warned for this module. */
+ goto return_none;
+ if (rc == 0) {
+ if (call_show_warning(tstate, category, text, message, filename,
+ lineno, lineno_obj, sourceline, source) < 0)
+ goto cleanup;
+ }
+ else /* if (rc == -1) */
+ goto cleanup;
+
+ return_none:
+ result = Py_None;
+ Py_INCREF(result);
+
+ cleanup:
+ Py_XDECREF(item);
+ Py_XDECREF(key);
+ Py_XDECREF(text);
+ Py_XDECREF(lineno_obj);
+ Py_DECREF(module);
+ Py_XDECREF(message);
+ return result; /* Py_None or NULL. */
+}
+
+static int
+is_internal_frame(PyFrameObject *frame)
+{
+ if (frame == NULL) {
+ return 0;
+ }
+
+ PyCodeObject *code = PyFrame_GetCode(frame);
+ PyObject *filename = code->co_filename;
+ Py_DECREF(code);
+
+ if (filename == NULL) {
+ return 0;
+ }
+ if (!PyUnicode_Check(filename)) {
+ return 0;
+ }
+
+ int contains = PyUnicode_Contains(filename, &_Py_ID(importlib));
+ if (contains < 0) {
+ return 0;
+ }
+ else if (contains > 0) {
+ contains = PyUnicode_Contains(filename, &_Py_ID(_bootstrap));
+ if (contains < 0) {
+ return 0;
+ }
+ else if (contains > 0) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static PyFrameObject *
+next_external_frame(PyFrameObject *frame)
+{
+ do {
+ PyFrameObject *back = PyFrame_GetBack(frame);
+ Py_DECREF(frame);
+ frame = back;
+ } while (frame != NULL && is_internal_frame(frame));
+
+ return frame;
+}
+
+/* filename, module, and registry are new refs, globals is borrowed */
+/* Returns 0 on error (no new refs), 1 on success */
+static int
+setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
+ PyObject **module, PyObject **registry)
+{
+ PyObject *globals;
+
+ /* Setup globals, filename and lineno. */
+ PyThreadState *tstate = get_current_tstate();
+ if (tstate == NULL) {
+ return 0;
+ }
+ PyInterpreterState *interp = tstate->interp;
+ PyFrameObject *f = PyThreadState_GetFrame(tstate);
+ // Stack level comparisons to Python code is off by one as there is no
+ // warnings-related stack level to avoid.
+ if (stack_level <= 0 || is_internal_frame(f)) {
+ while (--stack_level > 0 && f != NULL) {
+ PyFrameObject *back = PyFrame_GetBack(f);
+ Py_DECREF(f);
+ f = back;
+ }
+ }
+ else {
+ while (--stack_level > 0 && f != NULL) {
+ f = next_external_frame(f);
+ }
+ }
+
+ if (f == NULL) {
+ globals = interp->sysdict;
+ *filename = PyUnicode_FromString("sys");
+ *lineno = 1;
+ }
+ else {
+ globals = f->f_frame->f_globals;
+ *filename = f->f_frame->f_code->co_filename;
+ Py_INCREF(*filename);
+ *lineno = PyFrame_GetLineNumber(f);
+ Py_DECREF(f);
+ }
+
+ *module = NULL;
+
+ /* Setup registry. */
+ assert(globals != NULL);
+ assert(PyDict_Check(globals));
+ *registry = _PyDict_GetItemWithError(globals, &_Py_ID(__warningregistry__));
+ if (*registry == NULL) {
+ int rc;
+
+ if (_PyErr_Occurred(tstate)) {
+ goto handle_error;
+ }
+ *registry = PyDict_New();
+ if (*registry == NULL)
+ goto handle_error;
+
+ rc = PyDict_SetItem(globals, &_Py_ID(__warningregistry__), *registry);
+ if (rc < 0)
+ goto handle_error;
+ }
+ else
+ Py_INCREF(*registry);
+
+ /* Setup module. */
+ *module = _PyDict_GetItemWithError(globals, &_Py_ID(__name__));
+ if (*module == Py_None || (*module != NULL && PyUnicode_Check(*module))) {
+ Py_INCREF(*module);
+ }
+ else if (_PyErr_Occurred(tstate)) {
+ goto handle_error;
+ }
+ else {
+ *module = PyUnicode_FromString("<string>");
+ if (*module == NULL)
+ goto handle_error;
+ }
+
+ return 1;
+
+ handle_error:
+ Py_XDECREF(*registry);
+ Py_XDECREF(*module);
+ Py_DECREF(*filename);
+ return 0;
+}
+
+static PyObject *
+get_category(PyObject *message, PyObject *category)
+{
+ int rc;
+
+ /* Get category. */
+ rc = PyObject_IsInstance(message, PyExc_Warning);
+ if (rc == -1)
+ return NULL;
+
+ if (rc == 1)
+ category = (PyObject*)Py_TYPE(message);
+ else if (category == NULL || category == Py_None)
+ category = PyExc_UserWarning;
+
+ /* Validate category. */
+ rc = PyObject_IsSubclass(category, PyExc_Warning);
+ /* category is not a subclass of PyExc_Warning or
+ PyObject_IsSubclass raised an error */
+ if (rc == -1 || rc == 0) {
+ PyErr_Format(PyExc_TypeError,
+ "category must be a Warning subclass, not '%s'",
+ Py_TYPE(category)->tp_name);
+ return NULL;
+ }
+
+ return category;
+}
+
+static PyObject *
+do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level,
+ PyObject *source)
+{
+ PyObject *filename, *module, *registry, *res;
+ int lineno;
+
+ PyThreadState *tstate = get_current_tstate();
+ if (tstate == NULL) {
+ return NULL;
+ }
+
+ if (!setup_context(stack_level, &filename, &lineno, &module, &registry))
+ return NULL;
+
+ res = warn_explicit(tstate, category, message, filename, lineno, module, registry,
+ NULL, source);
+ Py_DECREF(filename);
+ Py_DECREF(registry);
+ Py_DECREF(module);
+ return res;
+}
+
+/*[clinic input]
+warn as warnings_warn
+
+ message: object
+ category: object = None
+ stacklevel: Py_ssize_t = 1
+ source: object = None
+
+Issue a warning, or maybe ignore it or raise an exception.
+[clinic start generated code]*/
+
+static PyObject *
+warnings_warn_impl(PyObject *module, PyObject *message, PyObject *category,
+ Py_ssize_t stacklevel, PyObject *source)
+/*[clinic end generated code: output=31ed5ab7d8d760b2 input=bfdf5cf99f6c4edd]*/
+{
+ category = get_category(message, category);
+ if (category == NULL)
+ return NULL;
+ return do_warn(message, category, stacklevel, source);
+}
+
+static PyObject *
+get_source_line(PyInterpreterState *interp, PyObject *module_globals, int lineno)
+{
+ PyObject *loader;
+ PyObject *module_name;
+ PyObject *get_source;
+ PyObject *source;
+ PyObject *source_list;
+ PyObject *source_line;
+
+ /* Check/get the requisite pieces needed for the loader. */
+ loader = _PyDict_GetItemWithError(module_globals, &_Py_ID(__loader__));
+ if (loader == NULL) {
+ return NULL;
+ }
+ Py_INCREF(loader);
+ module_name = _PyDict_GetItemWithError(module_globals, &_Py_ID(__name__));
+ if (!module_name) {
+ Py_DECREF(loader);
+ return NULL;
+ }
+ Py_INCREF(module_name);
+
+ /* Make sure the loader implements the optional get_source() method. */
+ (void)_PyObject_LookupAttr(loader, &_Py_ID(get_source), &get_source);
+ Py_DECREF(loader);
+ if (!get_source) {
+ Py_DECREF(module_name);
+ return NULL;
+ }
+ /* Call get_source() to get the source code. */
+ source = PyObject_CallOneArg(get_source, module_name);
+ Py_DECREF(get_source);
+ Py_DECREF(module_name);
+ if (!source) {
+ return NULL;
+ }
+ if (source == Py_None) {
+ Py_DECREF(source);
+ return NULL;
+ }
+
+ /* Split the source into lines. */
+ source_list = PyUnicode_Splitlines(source, 0);
+ Py_DECREF(source);
+ if (!source_list) {
+ return NULL;
+ }
+
+ /* Get the source line. */
+ source_line = PyList_GetItem(source_list, lineno-1);
+ Py_XINCREF(source_line);
+ Py_DECREF(source_list);
+ return source_line;
+}
+
+static PyObject *
+warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
+{
+ static char *kwd_list[] = {"message", "category", "filename", "lineno",
+ "module", "registry", "module_globals",
+ "source", 0};
+ PyObject *message;
+ PyObject *category;
+ PyObject *filename;
+ int lineno;
+ PyObject *module = NULL;
+ PyObject *registry = NULL;
+ PyObject *module_globals = NULL;
+ PyObject *sourceobj = NULL;
+ PyObject *source_line = NULL;
+ PyObject *returned;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOUi|OOOO:warn_explicit",
+ kwd_list, &message, &category, &filename, &lineno, &module,
+ &registry, &module_globals, &sourceobj))
+ return NULL;
+
+ PyThreadState *tstate = get_current_tstate();
+ if (tstate == NULL) {
+ return NULL;
+ }
+
+ if (module_globals && module_globals != Py_None) {
+ if (!PyDict_Check(module_globals)) {
+ PyErr_Format(PyExc_TypeError,
+ "module_globals must be a dict, not '%.200s'",
+ Py_TYPE(module_globals)->tp_name);
+ return NULL;
+ }
+
+ source_line = get_source_line(tstate->interp, module_globals, lineno);
+ if (source_line == NULL && PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ returned = warn_explicit(tstate, category, message, filename, lineno, module,
+ registry, source_line, sourceobj);
+ Py_XDECREF(source_line);
+ return returned;
+}
+
+static PyObject *
+warnings_filters_mutated(PyObject *self, PyObject *Py_UNUSED(args))
+{
+ PyInterpreterState *interp = get_current_interp();
+ if (interp == NULL) {
+ return NULL;
+ }
+ WarningsState *st = warnings_get_state(interp);
+ if (st == NULL) {
+ return NULL;
+ }
+ st->filters_version++;
+ Py_RETURN_NONE;
+}
+
+
+/* Function to issue a warning message; may raise an exception. */
+
+static int
+warn_unicode(PyObject *category, PyObject *message,
+ Py_ssize_t stack_level, PyObject *source)
+{
+ PyObject *res;
+
+ if (category == NULL)
+ category = PyExc_RuntimeWarning;
+
+ res = do_warn(message, category, stack_level, source);
+ if (res == NULL)
+ return -1;
+ Py_DECREF(res);
+
+ return 0;
+}
+
+static int
+_PyErr_WarnFormatV(PyObject *source,
+ PyObject *category, Py_ssize_t stack_level,
+ const char *format, va_list vargs)
+{
+ PyObject *message;
+ int res;
+
+ message = PyUnicode_FromFormatV(format, vargs);
+ if (message == NULL)
+ return -1;
+
+ res = warn_unicode(category, message, stack_level, source);
+ Py_DECREF(message);
+ return res;
+}
+
+int
+PyErr_WarnFormat(PyObject *category, Py_ssize_t stack_level,
+ const char *format, ...)
+{
+ int res;
+ va_list vargs;
+
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, format);
+#else
+ va_start(vargs);
+#endif
+ res = _PyErr_WarnFormatV(NULL, category, stack_level, format, vargs);
+ va_end(vargs);
+ return res;
+}
+
+static int
+_PyErr_WarnFormat(PyObject *source, PyObject *category, Py_ssize_t stack_level,
+ const char *format, ...)
+{
+ int res;
+ va_list vargs;
+
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, format);
+#else
+ va_start(vargs);
+#endif
+ res = _PyErr_WarnFormatV(source, category, stack_level, format, vargs);
+ va_end(vargs);
+ return res;
+}
+
+int
+PyErr_ResourceWarning(PyObject *source, Py_ssize_t stack_level,
+ const char *format, ...)
+{
+ int res;
+ va_list vargs;
+
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, format);
+#else
+ va_start(vargs);
+#endif
+ res = _PyErr_WarnFormatV(source, PyExc_ResourceWarning,
+ stack_level, format, vargs);
+ va_end(vargs);
+ return res;
+}
+
+
+int
+PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level)
+{
+ int ret;
+ PyObject *message = PyUnicode_FromString(text);
+ if (message == NULL)
+ return -1;
+ ret = warn_unicode(category, message, stack_level, NULL);
+ Py_DECREF(message);
+ return ret;
+}
+
+/* PyErr_Warn is only for backwards compatibility and will be removed.
+ Use PyErr_WarnEx instead. */
+
+#undef PyErr_Warn
+
+int
+PyErr_Warn(PyObject *category, const char *text)
+{
+ return PyErr_WarnEx(category, text, 1);
+}
+
+/* Warning with explicit origin */
+int
+PyErr_WarnExplicitObject(PyObject *category, PyObject *message,
+ PyObject *filename, int lineno,
+ PyObject *module, PyObject *registry)
+{
+ PyObject *res;
+ if (category == NULL)
+ category = PyExc_RuntimeWarning;
+ PyThreadState *tstate = get_current_tstate();
+ if (tstate == NULL) {
+ return -1;
+ }
+ res = warn_explicit(tstate, category, message, filename, lineno,
+ module, registry, NULL, NULL);
+ if (res == NULL)
+ return -1;
+ Py_DECREF(res);
+ return 0;
+}
+
+int
+PyErr_WarnExplicit(PyObject *category, const char *text,
+ const char *filename_str, int lineno,
+ const char *module_str, PyObject *registry)
+{
+ PyObject *message = PyUnicode_FromString(text);
+ if (message == NULL) {
+ return -1;
+ }
+ PyObject *filename = PyUnicode_DecodeFSDefault(filename_str);
+ if (filename == NULL) {
+ Py_DECREF(message);
+ return -1;
+ }
+ PyObject *module = NULL;
+ if (module_str != NULL) {
+ module = PyUnicode_FromString(module_str);
+ if (module == NULL) {
+ Py_DECREF(filename);
+ Py_DECREF(message);
+ return -1;
+ }
+ }
+
+ int ret = PyErr_WarnExplicitObject(category, message, filename, lineno,
+ module, registry);
+ Py_XDECREF(module);
+ Py_DECREF(filename);
+ Py_DECREF(message);
+ return ret;
+}
+
+int
+PyErr_WarnExplicitFormat(PyObject *category,
+ const char *filename_str, int lineno,
+ const char *module_str, PyObject *registry,
+ const char *format, ...)
+{
+ PyObject *message;
+ PyObject *module = NULL;
+ PyObject *filename = PyUnicode_DecodeFSDefault(filename_str);
+ int ret = -1;
+ va_list vargs;
+
+ if (filename == NULL)
+ goto exit;
+ if (module_str != NULL) {
+ module = PyUnicode_FromString(module_str);
+ if (module == NULL)
+ goto exit;
+ }
+
+#ifdef HAVE_STDARG_PROTOTYPES
+ va_start(vargs, format);
+#else
+ va_start(vargs);
+#endif
+ message = PyUnicode_FromFormatV(format, vargs);
+ if (message != NULL) {
+ PyObject *res;
+ PyThreadState *tstate = get_current_tstate();
+ if (tstate != NULL) {
+ res = warn_explicit(tstate, category, message, filename, lineno,
+ module, registry, NULL, NULL);
+ Py_DECREF(message);
+ if (res != NULL) {
+ Py_DECREF(res);
+ ret = 0;
+ }
+ }
+ }
+ va_end(vargs);
+exit:
+ Py_XDECREF(module);
+ Py_XDECREF(filename);
+ return ret;
+}
+
+void
+_PyErr_WarnUnawaitedCoroutine(PyObject *coro)
+{
+ /* First, we attempt to funnel the warning through
+ warnings._warn_unawaited_coroutine.
+
+ This could raise an exception, due to:
+ - a bug
+ - some kind of shutdown-related brokenness
+ - succeeding, but with an "error" warning filter installed, so the
+ warning is converted into a RuntimeWarning exception
+
+ In the first two cases, we want to print the error (so we know what it
+ is!), and then print a warning directly as a fallback. In the last
+ case, we want to print the error (since it's the warning!), but *not*
+ do a fallback. And after we print the error we can't check for what
+ type of error it was (because PyErr_WriteUnraisable clears it), so we
+ need a flag to keep track.
+
+ Since this is called from __del__ context, it's careful to never raise
+ an exception.
+ */
+ int warned = 0;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ assert(interp != NULL);
+ PyObject *fn = GET_WARNINGS_ATTR(interp, _warn_unawaited_coroutine, 1);
+ if (fn) {
+ PyObject *res = PyObject_CallOneArg(fn, coro);
+ Py_DECREF(fn);
+ if (res || PyErr_ExceptionMatches(PyExc_RuntimeWarning)) {
+ warned = 1;
+ }
+ Py_XDECREF(res);
+ }
+
+ if (PyErr_Occurred()) {
+ PyErr_WriteUnraisable(coro);
+ }
+ if (!warned) {
+ if (_PyErr_WarnFormat(coro, PyExc_RuntimeWarning, 1,
+ "coroutine '%S' was never awaited",
+ ((PyCoroObject *)coro)->cr_qualname) < 0)
+ {
+ PyErr_WriteUnraisable(coro);
+ }
+ }
+}
+
+PyDoc_STRVAR(warn_explicit_doc,
+"Low-level interface to warnings functionality.");
+
+static PyMethodDef warnings_functions[] = {
+ WARNINGS_WARN_METHODDEF
+ {"warn_explicit", _PyCFunction_CAST(warnings_warn_explicit),
+ METH_VARARGS | METH_KEYWORDS, warn_explicit_doc},
+ {"_filters_mutated", _PyCFunction_CAST(warnings_filters_mutated), METH_NOARGS,
+ NULL},
+ /* XXX(brett.cannon): add showwarning? */
+ /* XXX(brett.cannon): Reasonable to add formatwarning? */
+ {NULL, NULL} /* sentinel */
+};
+
+
+static int
+warnings_module_exec(PyObject *module)
+{
+ PyInterpreterState *interp = get_current_interp();
+ if (interp == NULL) {
+ return -1;
+ }
+ WarningsState *st = warnings_get_state(interp);
+ if (st == NULL) {
+ return -1;
+ }
+ if (PyModule_AddObjectRef(module, "filters", st->filters) < 0) {
+ return -1;
+ }
+ if (PyModule_AddObjectRef(module, "_onceregistry", st->once_registry) < 0) {
+ return -1;
+ }
+ if (PyModule_AddObjectRef(module, "_defaultaction", st->default_action) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+
+static PyModuleDef_Slot warnings_slots[] = {
+ {Py_mod_exec, warnings_module_exec},
+ {0, NULL}
+};
+
+static struct PyModuleDef warnings_module = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = MODULE_NAME,
+ .m_doc = warnings__doc__,
+ .m_size = 0,
+ .m_methods = warnings_functions,
+ .m_slots = warnings_slots,
+};
+
+
+PyMODINIT_FUNC
+_PyWarnings_Init(void)
+{
+ return PyModuleDef_Init(&warnings_module);
+}
+
+// We need this to ensure that warnings still work until late in finalization.
+void
+_PyWarnings_Fini(PyInterpreterState *interp)
+{
+ warnings_clear_state(&interp->warnings);
+}