_warnings.c revision 593daf545bd9b7e7bcb27b498ecc6f36db9ae395
1#include "Python.h"
2#include "frameobject.h"
3
4#define MODULE_NAME "_warnings"
5#define DEFAULT_ACTION_NAME "default_action"
6
7PyDoc_STRVAR(warnings__doc__,
8MODULE_NAME " provides basic warning filtering support.\n"
9"It is a helper module to speed up interpreter start-up.");
10
11/* Both 'filters' and 'onceregistry' can be set in warnings.py;
12   get_warnings_attr() will reset these variables accordingly. */
13static PyObject *_filters;  /* List */
14static PyObject *_once_registry;  /* Dict */
15
16
17static int
18check_matched(PyObject *obj, PyObject *arg)
19{
20    PyObject *result;
21    int rc;
22
23    if (obj == Py_None)
24        return 1;
25    result = PyObject_CallMethod(obj, "match", "O", arg);
26    if (result == NULL)
27        return -1;
28
29    rc = PyObject_IsTrue(result);
30    Py_DECREF(result);
31    return rc;
32}
33
34/*
35   Returns a new reference.
36   A NULL return value can mean false or an error.
37*/
38static PyObject *
39get_warnings_attr(const char *attr)
40{
41    static PyObject *warnings_str = NULL;
42    PyObject *all_modules;
43    PyObject *warnings_module;
44    int result;
45
46    if (warnings_str == NULL) {
47        warnings_str = PyBytes_InternFromString("warnings");
48        if (warnings_str == NULL)
49            return NULL;
50    }
51
52    all_modules = PyImport_GetModuleDict();
53    result = PyDict_Contains(all_modules, warnings_str);
54    if (result == -1 || result == 0)
55        return NULL;
56
57    warnings_module = PyDict_GetItem(all_modules, warnings_str);
58    if (!PyObject_HasAttrString(warnings_module, attr))
59            return NULL;
60    return PyObject_GetAttrString(warnings_module, attr);
61}
62
63
64static PyObject *
65get_once_registry(void)
66{
67    PyObject *registry;
68
69    registry = get_warnings_attr("onceregistry");
70    if (registry == NULL) {
71        if (PyErr_Occurred())
72            return NULL;
73        return _once_registry;
74    }
75    Py_DECREF(_once_registry);
76    _once_registry = registry;
77    return registry;
78}
79
80
81/* The item is a borrowed reference. */
82static const char *
83get_filter(PyObject *category, PyObject *text, Py_ssize_t lineno,
84           PyObject *module, PyObject **item)
85{
86    PyObject *action, *m, *d;
87    Py_ssize_t i;
88    PyObject *warnings_filters;
89
90    warnings_filters = get_warnings_attr("filters");
91    if (warnings_filters == NULL) {
92        if (PyErr_Occurred())
93            return NULL;
94    }
95    else {
96        Py_DECREF(_filters);
97        _filters = warnings_filters;
98    }
99
100    if (!PyList_Check(_filters)) {
101        PyErr_SetString(PyExc_ValueError,
102                        MODULE_NAME ".filters must be a list");
103        return NULL;
104    }
105
106    /* _filters could change while we are iterating over it. */
107    for (i = 0; i < PyList_GET_SIZE(_filters); i++) {
108        PyObject *tmp_item, *action, *msg, *cat, *mod, *ln_obj;
109        Py_ssize_t ln;
110        int is_subclass, good_msg, good_mod;
111
112        tmp_item = *item = PyList_GET_ITEM(_filters, i);
113        if (PyTuple_Size(tmp_item) != 5) {
114            PyErr_Format(PyExc_ValueError,
115                         MODULE_NAME ".filters item %zd isn't a 5-tuple", i);
116            return NULL;
117        }
118
119        /* Python code: action, msg, cat, mod, ln = item */
120        action = PyTuple_GET_ITEM(tmp_item, 0);
121        msg = PyTuple_GET_ITEM(tmp_item, 1);
122        cat = PyTuple_GET_ITEM(tmp_item, 2);
123        mod = PyTuple_GET_ITEM(tmp_item, 3);
124        ln_obj = PyTuple_GET_ITEM(tmp_item, 4);
125
126        good_msg = check_matched(msg, text);
127        good_mod = check_matched(mod, module);
128        is_subclass = PyObject_IsSubclass(category, cat);
129        ln = PyInt_AsSsize_t(ln_obj);
130        if (good_msg == -1 || good_mod == -1 || is_subclass == -1 ||
131            (ln == -1 && PyErr_Occurred()))
132            return NULL;
133
134        if (good_msg && is_subclass && good_mod && (ln == 0 || lineno == ln))
135            return PyBytes_AsString(action);
136    }
137
138    m = PyImport_ImportModule(MODULE_NAME);
139    if (m == NULL)
140        return NULL;
141    d = PyModule_GetDict(m);
142    Py_DECREF(m);
143    if (d == NULL)
144        return NULL;
145    action = PyDict_GetItemString(d, DEFAULT_ACTION_NAME);
146    if (action != NULL)
147        return PyBytes_AsString(action);
148
149    PyErr_SetString(PyExc_ValueError,
150                    MODULE_NAME "." DEFAULT_ACTION_NAME " not found");
151    return NULL;
152}
153
154static int
155already_warned(PyObject *registry, PyObject *key, int should_set)
156{
157    PyObject *already_warned;
158
159    if (key == NULL)
160        return -1;
161
162    already_warned = PyDict_GetItem(registry, key);
163    if (already_warned != NULL) {
164        int rc = PyObject_IsTrue(already_warned);
165        if (rc != 0)
166            return rc;
167    }
168
169    /* This warning wasn't found in the registry, set it. */
170    if (should_set)
171        return PyDict_SetItem(registry, key, Py_True);
172    return 0;
173}
174
175/* New reference. */
176static PyObject *
177normalize_module(PyObject *filename)
178{
179    PyObject *module;
180    const char *mod_str;
181    Py_ssize_t len;
182
183    int rc = PyObject_IsTrue(filename);
184    if (rc == -1)
185        return NULL;
186    else if (rc == 0)
187        return PyBytes_FromString("<unknown>");
188
189    mod_str = PyBytes_AsString(filename);
190    if (mod_str == NULL)
191	    return NULL;
192    len = PyBytes_Size(filename);
193    if (len < 0)
194        return NULL;
195    if (len >= 3 &&
196	strncmp(mod_str + (len - 3), ".py", 3) == 0) {
197        module = PyBytes_FromStringAndSize(mod_str, len-3);
198    }
199    else {
200        module = filename;
201        Py_INCREF(module);
202    }
203    return module;
204}
205
206static int
207update_registry(PyObject *registry, PyObject *text, PyObject *category,
208                int add_zero)
209{
210    PyObject *altkey, *zero = NULL;
211    int rc;
212
213    if (add_zero) {
214        zero = PyInt_FromLong(0);
215        if (zero == NULL)
216            return -1;
217        altkey = PyTuple_Pack(3, text, category, zero);
218    }
219    else
220        altkey = PyTuple_Pack(2, text, category);
221
222    rc = already_warned(registry, altkey, 1);
223    Py_XDECREF(zero);
224    Py_XDECREF(altkey);
225    return rc;
226}
227
228static void
229show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
230                *category, PyObject *sourceline)
231{
232    PyObject *f_stderr;
233    PyObject *name;
234    char lineno_str[128];
235
236    PyOS_snprintf(lineno_str, sizeof(lineno_str), ":%d: ", lineno);
237
238    name = PyObject_GetAttrString(category, "__name__");
239    if (name == NULL)  /* XXX Can an object lack a '__name__' attribute? */
240	    return;
241
242    f_stderr = PySys_GetObject("stderr");
243    if (f_stderr == NULL) {
244        fprintf(stderr, "lost sys.stderr\n");
245        Py_DECREF(name);
246        return;
247    }
248
249    /* Print "filename:lineno: category: text\n" */
250    PyFile_WriteObject(filename, f_stderr, Py_PRINT_RAW);
251    PyFile_WriteString(lineno_str, f_stderr);
252    PyFile_WriteObject(name, f_stderr, Py_PRINT_RAW);
253    PyFile_WriteString(": ", f_stderr);
254    PyFile_WriteObject(text, f_stderr, Py_PRINT_RAW);
255    PyFile_WriteString("\n", f_stderr);
256    Py_XDECREF(name);
257
258    /* Print "  source_line\n" */
259    PyFile_WriteString("  ", f_stderr);
260    if (sourceline) {
261        char *source_line_str = PyBytes_AS_STRING(sourceline);
262        while (*source_line_str == ' ' || *source_line_str == '\t' ||
263                *source_line_str == '\014')
264            source_line_str++;
265
266        PyFile_WriteString(source_line_str, f_stderr);
267        PyFile_WriteString("\n", f_stderr);
268    }
269    else
270        Py_DisplaySourceLine(f_stderr, PyBytes_AS_STRING(filename), lineno);
271    PyErr_Clear();
272}
273
274static PyObject *
275warn_explicit(PyObject *category, PyObject *message,
276              PyObject *filename, int lineno,
277              PyObject *module, PyObject *registry, PyObject *sourceline)
278{
279    PyObject *key = NULL, *text = NULL, *result = NULL, *lineno_obj = NULL;
280    PyObject *item = Py_None;
281    const char *action;
282    int rc;
283
284    /* Normalize module. */
285    if (module == NULL) {
286        module = normalize_module(filename);
287        if (module == NULL)
288            return NULL;
289    }
290    else
291        Py_INCREF(module);
292
293    /* Normalize message. */
294    Py_INCREF(message);  /* DECREF'ed in cleanup. */
295    rc = PyObject_IsInstance(message, PyExc_Warning);
296    if (rc == -1) {
297        goto cleanup;
298    }
299    if (rc == 1) {
300        text = PyObject_Str(message);
301        category = (PyObject*)message->ob_type;
302    }
303    else {
304        text = message;
305        message = PyObject_CallFunction(category, "O", message);
306    }
307
308    lineno_obj = PyInt_FromLong(lineno);
309    if (lineno_obj == NULL)
310        goto cleanup;
311
312    /* Create key. */
313    key = PyTuple_Pack(3, text, category, lineno_obj);
314    if (key == NULL)
315        goto cleanup;
316
317    if (registry != NULL) {
318        rc = already_warned(registry, key, 0);
319        if (rc == -1)
320            goto cleanup;
321	else if (rc == 1)
322            goto return_none;
323        /* Else this warning hasn't been generated before. */
324    }
325
326    action = get_filter(category, text, lineno, module, &item);
327    if (action == NULL)
328        goto cleanup;
329
330    if (strcmp(action, "error") == 0) {
331        PyErr_SetObject(category, message);
332        goto cleanup;
333    }
334
335    /* Store in the registry that we've been here, *except* when the action
336       is "always". */
337    rc = 0;
338    if (strcmp(action, "always") != 0) {
339        if (registry != NULL && PyDict_SetItem(registry, key, Py_True) < 0)
340            goto cleanup;
341        else if (strcmp(action, "ignore") == 0)
342            goto return_none;
343        else if (strcmp(action, "once") == 0) {
344            if (registry == NULL) {
345                registry = get_once_registry();
346                if (registry == NULL)
347                    goto cleanup;
348            }
349            /* _once_registry[(text, category)] = 1 */
350            rc = update_registry(registry, text, category, 0);
351        }
352        else if (strcmp(action, "module") == 0) {
353            /* registry[(text, category, 0)] = 1 */
354            if (registry != NULL)
355                rc = update_registry(registry, text, category, 0);
356        }
357        else if (strcmp(action, "default") != 0) {
358            PyObject *to_str = PyObject_Str(item);
359            const char *err_str = "???";
360
361            if (to_str != NULL)
362                err_str = PyBytes_AS_STRING(to_str);
363            PyErr_Format(PyExc_RuntimeError,
364                        "Unrecognized action (%s) in warnings.filters:\n %s",
365                        action, err_str);
366            Py_XDECREF(to_str);
367            goto cleanup;
368        }
369    }
370
371    if (rc == 1)  // Already warned for this module. */
372        goto return_none;
373    if (rc == 0) {
374        PyObject *show_fxn = get_warnings_attr("showwarning");
375        if (show_fxn == NULL) {
376            if (PyErr_Occurred())
377                goto cleanup;
378            show_warning(filename, lineno, text, category, sourceline);
379        }
380        else {
381            const char *msg = "functions overriding warnings.showwarning() "
382                                "must support the 'line' argument";
383            const char *text_char = PyBytes_AS_STRING(text);
384
385            if (strcmp(msg, text_char) == 0) {
386                /* Prevent infinite recursion by using built-in implementation
387                   of showwarning(). */
388                show_warning(filename, lineno, text, category, sourceline);
389            }
390            else {
391                PyObject *check_fxn;
392                PyObject *defaults;
393                PyObject *res;
394
395                if (PyMethod_Check(show_fxn))
396                    check_fxn = PyMethod_Function(show_fxn);
397                else if (PyFunction_Check(show_fxn))
398                    check_fxn = show_fxn;
399                else {
400                    PyErr_SetString(PyExc_TypeError,
401                                    "warnings.showwarning() must be set to a "
402                                    "function or method");
403                    Py_DECREF(show_fxn);
404                    goto cleanup;
405                }
406
407                defaults = PyFunction_GetDefaults(check_fxn);
408                /* A proper implementation of warnings.showwarning() should
409                    have at least two default arguments. */
410                if ((defaults == NULL) || (PyTuple_Size(defaults) < 2)) {
411                    if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1) < 0) {
412                        Py_DECREF(show_fxn);
413                        goto cleanup;
414                    }
415                }
416                res = PyObject_CallFunctionObjArgs(show_fxn, message, category,
417                                                    filename, lineno_obj,
418                                                    NULL);
419                Py_DECREF(show_fxn);
420                Py_XDECREF(res);
421                if (res == NULL)
422                    goto cleanup;
423            }
424        }
425    }
426    else /* if (rc == -1) */
427        goto cleanup;
428
429 return_none:
430    result = Py_None;
431    Py_INCREF(result);
432
433 cleanup:
434    Py_XDECREF(key);
435    Py_XDECREF(text);
436    Py_XDECREF(lineno_obj);
437    Py_DECREF(module);
438    Py_DECREF(message);
439    return result;  /* Py_None or NULL. */
440}
441
442/* filename, module, and registry are new refs, globals is borrowed */
443/* Returns 0 on error (no new refs), 1 on success */
444static int
445setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
446              PyObject **module, PyObject **registry)
447{
448    PyObject *globals;
449
450    /* Setup globals and lineno. */
451    PyFrameObject *f = PyThreadState_GET()->frame;
452    while (--stack_level > 0 && f != NULL)
453        f = f->f_back;
454
455    if (f == NULL) {
456        globals = PyThreadState_Get()->interp->sysdict;
457        *lineno = 1;
458    }
459    else {
460        globals = f->f_globals;
461        *lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
462    }
463
464    *module = NULL;
465
466    /* Setup registry. */
467    assert(globals != NULL);
468    assert(PyDict_Check(globals));
469    *registry = PyDict_GetItemString(globals, "__warningregistry__");
470    if (*registry == NULL) {
471        int rc;
472
473        *registry = PyDict_New();
474        if (*registry == NULL)
475            return 0;
476
477         rc = PyDict_SetItemString(globals, "__warningregistry__", *registry);
478         if (rc < 0)
479            goto handle_error;
480    }
481    else
482        Py_INCREF(*registry);
483
484    /* Setup module. */
485    *module = PyDict_GetItemString(globals, "__name__");
486    if (*module == NULL) {
487        *module = PyBytes_FromString("<string>");
488        if (*module == NULL)
489            goto handle_error;
490    }
491    else
492        Py_INCREF(*module);
493
494    /* Setup filename. */
495    *filename = PyDict_GetItemString(globals, "__file__");
496    if (*filename != NULL) {
497	    Py_ssize_t len = PyBytes_Size(*filename);
498        const char *file_str = PyBytes_AsString(*filename);
499	    if (file_str == NULL || (len < 0 && PyErr_Occurred()))
500            goto handle_error;
501
502        /* if filename.lower().endswith((".pyc", ".pyo")): */
503        if (len >= 4 &&
504            file_str[len-4] == '.' &&
505            tolower(file_str[len-3]) == 'p' &&
506            tolower(file_str[len-2]) == 'y' &&
507            (tolower(file_str[len-1]) == 'c' ||
508                tolower(file_str[len-1]) == 'o'))
509        {
510            *filename = PyBytes_FromStringAndSize(file_str, len-1);
511	        if (*filename == NULL)
512		        goto handle_error;
513	    }
514	    else
515            Py_INCREF(*filename);
516    }
517    else {
518        const char *module_str = PyBytes_AsString(*module);
519        if (module_str && strcmp(module_str, "__main__") == 0) {
520            PyObject *argv = PySys_GetObject("argv");
521            if (argv != NULL && PyList_Size(argv) > 0) {
522                int is_true;
523                *filename = PyList_GetItem(argv, 0);
524                Py_INCREF(*filename);
525                /* If sys.argv[0] is false, then use '__main__'. */
526                is_true = PyObject_IsTrue(*filename);
527                if (is_true < 0) {
528                    Py_DECREF(*filename);
529                    goto handle_error;
530                }
531                else if (!is_true) {
532                    Py_DECREF(*filename);
533                    *filename = PyBytes_FromString("__main__");
534                    if (*filename == NULL)
535                        goto handle_error;
536                }
537            }
538            else {
539                /* embedded interpreters don't have sys.argv, see bug #839151 */
540                *filename = PyBytes_FromString("__main__");
541	            if (*filename == NULL)
542	                goto handle_error;
543            }
544        }
545        if (*filename == NULL) {
546            *filename = *module;
547            Py_INCREF(*filename);
548        }
549    }
550
551    return 1;
552
553 handle_error:
554    /* filename not XDECREF'ed here as there is no way to jump here with a
555       dangling reference. */
556    Py_XDECREF(*registry);
557    Py_XDECREF(*module);
558    return 0;
559}
560
561static PyObject *
562get_category(PyObject *message, PyObject *category)
563{
564    int rc;
565
566    /* Get category. */
567    rc = PyObject_IsInstance(message, PyExc_Warning);
568    if (rc == -1)
569        return NULL;
570
571    if (rc == 1)
572        category = (PyObject*)message->ob_type;
573    else if (category == NULL)
574        category = PyExc_UserWarning;
575
576    /* Validate category. */
577    rc = PyObject_IsSubclass(category, PyExc_Warning);
578    if (rc == -1)
579        return NULL;
580    if (rc == 0) {
581        PyErr_SetString(PyExc_ValueError,
582                        "category is not a subclass of Warning");
583        return NULL;
584    }
585
586    return category;
587}
588
589static PyObject *
590do_warn(PyObject *message, PyObject *category, Py_ssize_t stack_level)
591{
592    PyObject *filename, *module, *registry, *res;
593    int lineno;
594
595    if (!setup_context(stack_level, &filename, &lineno, &module, &registry))
596        return NULL;
597
598    res = warn_explicit(category, message, filename, lineno, module, registry,
599                        NULL);
600    Py_DECREF(filename);
601    Py_DECREF(registry);
602    Py_DECREF(module);
603    return res;
604}
605
606static PyObject *
607warnings_warn(PyObject *self, PyObject *args, PyObject *kwds)
608{
609    static char *kw_list[] = { "message", "category", "stacklevel", 0 };
610    PyObject *message, *category = NULL;
611    Py_ssize_t stack_level = 1;
612
613    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|On:warn", kw_list,
614                                     &message, &category, &stack_level))
615        return NULL;
616
617    category = get_category(message, category);
618    if (category == NULL)
619        return NULL;
620    return do_warn(message, category, stack_level);
621}
622
623static PyObject *
624warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
625{
626    static char *kwd_list[] = {"message", "category", "filename", "lineno",
627                                "module", "registry", "module_globals", 0};
628    PyObject *message;
629    PyObject *category;
630    PyObject *filename;
631    int lineno;
632    PyObject *module = NULL;
633    PyObject *registry = NULL;
634    PyObject *module_globals = NULL;
635
636    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOi|OOO:warn_explicit",
637                kwd_list, &message, &category, &filename, &lineno, &module,
638                &registry, &module_globals))
639        return NULL;
640
641    if (module_globals) {
642        static PyObject *get_source_name = NULL;
643        static PyObject *splitlines_name = NULL;
644        PyObject *loader;
645        PyObject *module_name;
646        PyObject *source;
647        PyObject *source_list;
648        PyObject *source_line;
649        PyObject *returned;
650
651        if (get_source_name == NULL) {
652            get_source_name = PyBytes_InternFromString("get_source");
653            if (!get_source_name)
654                return NULL;
655        }
656        if (splitlines_name == NULL) {
657            splitlines_name = PyBytes_InternFromString("splitlines");
658            if (!splitlines_name)
659                return NULL;
660        }
661
662        /* Check/get the requisite pieces needed for the loader. */
663        loader = PyDict_GetItemString(module_globals, "__loader__");
664        module_name = PyDict_GetItemString(module_globals, "__name__");
665
666        if (loader == NULL || module_name == NULL)
667            goto standard_call;
668
669        /* Make sure the loader implements the optional get_source() method. */
670        if (!PyObject_HasAttrString(loader, "get_source"))
671                goto standard_call;
672        /* Call get_source() to get the source code. */
673        source = PyObject_CallMethodObjArgs(loader, get_source_name,
674                                                module_name, NULL);
675        if (!source)
676            return NULL;
677        else if (source == Py_None) {
678            Py_DECREF(Py_None);
679            goto standard_call;
680        }
681
682        /* Split the source into lines. */
683        source_list = PyObject_CallMethodObjArgs(source, splitlines_name,
684                                                    NULL);
685        Py_DECREF(source);
686        if (!source_list)
687            return NULL;
688
689        /* Get the source line. */
690        source_line = PyList_GetItem(source_list, lineno-1);
691        if (!source_line) {
692            Py_DECREF(source_list);
693            return NULL;
694        }
695
696        /* Handle the warning. */
697        returned = warn_explicit(category, message, filename, lineno, module,
698                            registry, source_line);
699        Py_DECREF(source_list);
700        return returned;
701    }
702
703 standard_call:
704    return warn_explicit(category, message, filename, lineno, module,
705                                registry, NULL);
706}
707
708
709/* Function to issue a warning message; may raise an exception. */
710int
711PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level)
712{
713    PyObject *res;
714    PyObject *message = PyBytes_FromString(text);
715    if (message == NULL)
716        return -1;
717
718    if (category == NULL)
719        category = PyExc_RuntimeWarning;
720
721    res = do_warn(message, category, stack_level);
722    Py_DECREF(message);
723    if (res == NULL)
724        return -1;
725    Py_DECREF(res);
726
727    return 0;
728}
729
730/* PyErr_Warn is only for backwards compatability and will be removed.
731   Use PyErr_WarnEx instead. */
732
733#undef PyErr_Warn
734
735PyAPI_FUNC(int)
736PyErr_Warn(PyObject *category, char *text)
737{
738    return PyErr_WarnEx(category, text, 1);
739}
740
741/* Warning with explicit origin */
742int
743PyErr_WarnExplicit(PyObject *category, const char *text,
744                   const char *filename_str, int lineno,
745                   const char *module_str, PyObject *registry)
746{
747    PyObject *res;
748    PyObject *message = PyBytes_FromString(text);
749    PyObject *filename = PyBytes_FromString(filename_str);
750    PyObject *module = NULL;
751    int ret = -1;
752
753    if (message == NULL || filename == NULL)
754        goto exit;
755    if (module_str != NULL) {
756        module = PyBytes_FromString(module_str);
757            if (module == NULL)
758                goto exit;
759    }
760
761    if (category == NULL)
762        category = PyExc_RuntimeWarning;
763    res = warn_explicit(category, message, filename, lineno, module, registry,
764                        NULL);
765    if (res == NULL)
766        goto exit;
767    Py_DECREF(res);
768    ret = 0;
769
770 exit:
771    Py_XDECREF(message);
772    Py_XDECREF(module);
773    Py_XDECREF(filename);
774    return ret;
775}
776
777
778int
779PyErr_WarnPy3k(const char *text, Py_ssize_t stacklevel)
780{
781    if (Py_Py3kWarningFlag)
782        return PyErr_WarnEx(PyExc_DeprecationWarning, text, stacklevel);
783    return 0;
784}
785
786
787PyDoc_STRVAR(warn_doc,
788"Issue a warning, or maybe ignore it or raise an exception.");
789
790PyDoc_STRVAR(warn_explicit_doc,
791"Low-level inferface to warnings functionality.");
792
793static PyMethodDef warnings_functions[] = {
794    {"warn", (PyCFunction)warnings_warn, METH_VARARGS | METH_KEYWORDS,
795        warn_doc},
796    {"warn_explicit", (PyCFunction)warnings_warn_explicit,
797        METH_VARARGS | METH_KEYWORDS, warn_explicit_doc},
798    // XXX(brett.cannon): add showwarning?
799    // XXX(brett.cannon): Reasonable to add formatwarning?
800    {NULL, NULL}	        /* sentinel */
801};
802
803
804static PyObject *
805create_filter(PyObject *category, const char *action)
806{
807    static PyObject *ignore_str = NULL;
808    static PyObject *error_str = NULL;
809    static PyObject *default_str = NULL;
810    PyObject *action_obj = NULL;
811    PyObject *lineno, *result;
812
813    if (!strcmp(action, "ignore")) {
814        if (ignore_str == NULL) {
815            ignore_str = PyBytes_InternFromString("ignore");
816            if (ignore_str == NULL)
817                return NULL;
818        }
819        action_obj = ignore_str;
820    }
821    else if (!strcmp(action, "error")) {
822        if (error_str == NULL) {
823            error_str = PyBytes_InternFromString("error");
824            if (error_str == NULL)
825                return NULL;
826        }
827        action_obj = error_str;
828    }
829    else if (!strcmp(action, "default")) {
830        if (default_str == NULL) {
831            default_str = PyBytes_InternFromString("default");
832            if (default_str == NULL)
833                return NULL;
834        }
835        action_obj = default_str;
836    }
837    else {
838        Py_FatalError("unknown action");
839    }
840
841    /* This assumes the line number is zero for now. */
842    lineno = PyInt_FromLong(0);
843    if (lineno == NULL)
844        return NULL;
845    result = PyTuple_Pack(5, action_obj, Py_None, category, Py_None, lineno);
846    Py_DECREF(lineno);
847    return result;
848}
849
850static PyObject *
851init_filters(void)
852{
853    PyObject *filters = PyList_New(3);
854    const char *bytes_action;
855    if (filters == NULL)
856        return NULL;
857
858    PyList_SET_ITEM(filters, 0,
859                    create_filter(PyExc_PendingDeprecationWarning, "ignore"));
860    PyList_SET_ITEM(filters, 1, create_filter(PyExc_ImportWarning, "ignore"));
861    if (Py_BytesWarningFlag > 1)
862        bytes_action = "error";
863    else if (Py_BytesWarningFlag)
864        bytes_action = "default";
865    else
866        bytes_action = "ignore";
867    PyList_SET_ITEM(filters, 2, create_filter(PyExc_BytesWarning,
868                    bytes_action));
869
870    if (PyList_GET_ITEM(filters, 0) == NULL ||
871        PyList_GET_ITEM(filters, 1) == NULL ||
872        PyList_GET_ITEM(filters, 2) == NULL) {
873        Py_DECREF(filters);
874        return NULL;
875    }
876
877    return filters;
878}
879
880
881PyMODINIT_FUNC
882_PyWarnings_Init(void)
883{
884    PyObject *m, *default_action;
885
886    m = Py_InitModule3(MODULE_NAME, warnings_functions, warnings__doc__);
887    if (m == NULL)
888        return;
889
890    _filters = init_filters();
891    if (_filters == NULL)
892        return;
893    Py_INCREF(_filters);
894    if (PyModule_AddObject(m, "filters", _filters) < 0)
895        return;
896
897    _once_registry = PyDict_New();
898    if (_once_registry == NULL)
899        return;
900    Py_INCREF(_once_registry);
901    if (PyModule_AddObject(m, "once_registry", _once_registry) < 0)
902        return;
903
904    default_action = PyBytes_InternFromString("default");
905    if (default_action == NULL)
906        return;
907    if (PyModule_AddObject(m, DEFAULT_ACTION_NAME, default_action) < 0)
908        return;
909}
910