#include "Python.h"
#include "frameobject.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.");
/* Both 'filters' and 'onceregistry' can be set in warnings.py;
get_warnings_attr() will reset these variables accordingly. */
static PyObject *_filters; /* List */
static PyObject *_once_registry; /* Dict */
static PyObject *_default_action; /* String */
static long _filters_version;
_Py_IDENTIFIER(argv);
_Py_IDENTIFIER(stderr);
static int
check_matched(PyObject *obj, PyObject *arg)
{
PyObject *result;
_Py_IDENTIFIER(match);
int rc;
if (obj == Py_None)
return 1;
result = _PyObject_CallMethodId(obj, &PyId_match, "O", arg);
if (result == NULL)
return -1;
rc = PyObject_IsTrue(result);
Py_DECREF(result);
return rc;
}
/*
Returns a new reference.
A NULL return value can mean false or an error.
*/
static PyObject *
get_warnings_attr(const char *attr)
{
static PyObject *warnings_str = NULL;
PyObject *all_modules;
PyObject *warnings_module;
int result;
if (warnings_str == NULL) {
warnings_str = PyUnicode_InternFromString("warnings");
if (warnings_str == NULL)
return NULL;
}
all_modules = PyImport_GetModuleDict();
result = PyDict_Contains(all_modules, warnings_str);
if (result == -1 || result == 0)
return NULL;
warnings_module = PyDict_GetItem(all_modules, warnings_str);
if (!PyObject_HasAttrString(warnings_module, attr))
return NULL;
return PyObject_GetAttrString(warnings_module, attr);
}
static PyObject *
get_once_registry(void)
{
PyObject *registry;
registry = get_warnings_attr("onceregistry");
if (registry == NULL) {
if (PyErr_Occurred())
return NULL;
return _once_registry;
}
Py_DECREF(_once_registry);
_once_registry = registry;
return registry;
}
static PyObject *
get_default_action(void)
{
PyObject *default_action;
default_action = get_warnings_attr("defaultaction");
if (default_action == NULL) {
if (PyErr_Occurred()) {
return NULL;
}
return _default_action;
}
Py_DECREF(_default_action);
_default_action = default_action;
return default_action;
}
/* The item is a new reference. */
static PyObject*
get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
PyObject *module, PyObject **item)
{
PyObject *action;
Py_ssize_t i;
PyObject *warnings_filters;
warnings_filters = get_warnings_attr("filters");
if (warnings_filters == NULL) {
if (PyErr_Occurred())
return NULL;
}
else {
Py_DECREF(_filters);
_filters = warnings_filters;
}
if (_filters == NULL || !PyList_Check(_filters)) {
PyErr_SetString(PyExc_ValueError,
MODULE_NAME ".filters must be a list");
return NULL;
}
/* _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);
good_msg = check_matched(msg, text);
if (good_msg == -1) {
Py_DECREF(tmp_item);
return NULL;
}
good_mod = check_matched(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();
if (action != NULL) {
Py_INCREF(Py_None);
*item = Py_None;
return action;
}
PyErr_SetString(PyExc_ValueError,
MODULE_NAME ".defaultaction not found");
return NULL;
}
static int
already_warned(PyObject *registry, PyObject *key, int should_set)
{
PyObject *version_obj, *already_warned;
_Py_IDENTIFIER(version);
if (key == NULL)
return -1;
version_obj = _PyDict_GetItemId(registry, &PyId_version);
if (version_obj == NULL
|| !PyLong_CheckExact(version_obj)
|| PyLong_AsLong(version_obj) != _filters_version) {
PyDict_Clear(registry);
version_obj = PyLong_FromLong(_filters_version);
if (version_obj == NULL)
return -1;
if (_PyDict_SetItemId(registry, &PyId_version, version_obj) < 0) {
Py_DECREF(version_obj);
return -1;
}
Py_DECREF(version_obj);
}
else {
already_warned = PyDict_GetItem(registry, key);
if (already_warned != NULL) {
int rc = PyObject_IsTrue(already_warned);
if (rc != 0)
return rc;
}
}
/* 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;
void *data;
Py_ssize_t len;
len = PyUnicode_GetLength(filename);
if (len < 0)
return NULL;
if (len == 0)
return PyUnicode_FromString("");
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(PyObject *registry, PyObject *text, PyObject *category,
int add_zero)
{
PyObject *altkey, *zero = NULL;
int rc;
if (add_zero) {
zero = PyLong_FromLong(0);
if (zero == NULL)
return -1;
altkey = PyTuple_Pack(3, text, category, zero);
}
else
altkey = PyTuple_Pack(2, text, category);
rc = already_warned(registry, altkey, 1);
Py_XDECREF(zero);
Py_XDECREF(altkey);
return rc;
}
static void
show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
*category, PyObject *sourceline)
{
PyObject *f_stderr;
PyObject *name;
char lineno_str[128];
_Py_IDENTIFIER(__name__);
PyOS_snprintf(lineno_str, sizeof(lineno_str), ":%d: ", lineno);
name = _PyObject_GetAttrId(category, &PyId___name__);
if (name == NULL) /* XXX Can an object lack a '__name__' attribute? */
goto error;
f_stderr = _PySys_GetObjectId(&PyId_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;
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