[Python-checkins] bpo-33375: Get filename for warnings from frame.f_code.co_filename (GH-6622)

Brett Cannon webhook-mailer at python.org
Fri Jun 8 15:28:40 EDT 2018


https://github.com/python/cpython/commit/11a896652ee98aa44e59ed25237f9efb56635dcf
commit: 11a896652ee98aa44e59ed25237f9efb56635dcf
branch: master
author: Thomas Kluyver <takowl at gmail.com>
committer: Brett Cannon <brettcannon at users.noreply.github.com>
date: 2018-06-08T12:28:37-07:00
summary:

bpo-33375: Get filename for warnings from frame.f_code.co_filename (GH-6622)

More consistent with how other parts of Python find the filename (e.g. tracebacks and pdb).

files:
A Misc/NEWS.d/next/Library/2018-04-28-08-11-35.bpo-33375.Dbq1fz.rst
M Lib/test/test_warnings/__init__.py
M Lib/warnings.py
M Python/_warnings.c

diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index b48debd31fa8..71f6a3003414 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -441,78 +441,14 @@ def test_stacklevel_import(self):
                 self.assertEqual(len(w), 1)
                 self.assertEqual(w[0].filename, __file__)
 
-    def test_missing_filename_not_main(self):
-        # If __file__ is not specified and __main__ is not the module name,
-        # then __file__ should be set to the module name.
-        filename = warning_tests.__file__
-        try:
-            del warning_tests.__file__
-            with warnings_state(self.module):
-                with original_warnings.catch_warnings(record=True,
-                        module=self.module) as w:
-                    warning_tests.inner("spam8", stacklevel=1)
-                    self.assertEqual(w[-1].filename, warning_tests.__name__)
-        finally:
-            warning_tests.__file__ = filename
-
-    @unittest.skipUnless(hasattr(sys, 'argv'), 'test needs sys.argv')
-    def test_missing_filename_main_with_argv(self):
-        # If __file__ is not specified and the caller is __main__ and sys.argv
-        # exists, then use sys.argv[0] as the file.
-        filename = warning_tests.__file__
-        module_name = warning_tests.__name__
-        try:
-            del warning_tests.__file__
-            warning_tests.__name__ = '__main__'
-            with warnings_state(self.module):
-                with original_warnings.catch_warnings(record=True,
-                        module=self.module) as w:
-                    warning_tests.inner('spam9', stacklevel=1)
-                    self.assertEqual(w[-1].filename, sys.argv[0])
-        finally:
-            warning_tests.__file__ = filename
-            warning_tests.__name__ = module_name
-
-    def test_missing_filename_main_without_argv(self):
-        # If __file__ is not specified, the caller is __main__, and sys.argv
-        # is not set, then '__main__' is the file name.
-        filename = warning_tests.__file__
-        module_name = warning_tests.__name__
-        argv = sys.argv
-        try:
-            del warning_tests.__file__
-            warning_tests.__name__ = '__main__'
-            del sys.argv
-            with warnings_state(self.module):
-                with original_warnings.catch_warnings(record=True,
-                        module=self.module) as w:
-                    warning_tests.inner('spam10', stacklevel=1)
-                    self.assertEqual(w[-1].filename, '__main__')
-        finally:
-            warning_tests.__file__ = filename
-            warning_tests.__name__ = module_name
-            sys.argv = argv
-
-    def test_missing_filename_main_with_argv_empty_string(self):
-        # If __file__ is not specified, the caller is __main__, and sys.argv[0]
-        # is the empty string, then '__main__ is the file name.
-        # Tests issue 2743.
-        file_name = warning_tests.__file__
-        module_name = warning_tests.__name__
-        argv = sys.argv
-        try:
-            del warning_tests.__file__
-            warning_tests.__name__ = '__main__'
-            sys.argv = ['']
-            with warnings_state(self.module):
-                with original_warnings.catch_warnings(record=True,
-                        module=self.module) as w:
-                    warning_tests.inner('spam11', stacklevel=1)
-                    self.assertEqual(w[-1].filename, '__main__')
-        finally:
-            warning_tests.__file__ = file_name
-            warning_tests.__name__ = module_name
-            sys.argv = argv
+    def test_exec_filename(self):
+        filename = "<warnings-test>"
+        codeobj = compile(("import warnings\n"
+                           "warnings.warn('hello', UserWarning)"),
+                          filename, "exec")
+        with original_warnings.catch_warnings(record=True) as w:
+            exec(codeobj)
+        self.assertEqual(w[0].filename, filename)
 
     def test_warn_explicit_non_ascii_filename(self):
         with original_warnings.catch_warnings(record=True,
@@ -1245,9 +1181,7 @@ def __del__(self):
 a=A()
         """
         rc, out, err = assert_python_ok("-c", code)
-        # note: "__main__" filename is not correct, it should be the name
-        # of the script
-        self.assertEqual(err.decode(), '__main__:7: UserWarning: test')
+        self.assertEqual(err.decode(), '<string>:7: UserWarning: test')
 
     def test_late_resource_warning(self):
         # Issue #21925: Emitting a ResourceWarning late during the Python
diff --git a/Lib/warnings.py b/Lib/warnings.py
index 81f98647786d..6830b602de9a 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -303,28 +303,16 @@ def warn(message, category=None, stacklevel=1, source=None):
                     raise ValueError
     except ValueError:
         globals = sys.__dict__
+        filename = "sys"
         lineno = 1
     else:
         globals = frame.f_globals
+        filename = frame.f_code.co_filename
         lineno = frame.f_lineno
     if '__name__' in globals:
         module = globals['__name__']
     else:
         module = "<string>"
-    filename = globals.get('__file__')
-    if filename:
-        fnl = filename.lower()
-        if fnl.endswith(".pyc"):
-            filename = filename[:-1]
-    else:
-        if module == "__main__":
-            try:
-                filename = sys.argv[0]
-            except AttributeError:
-                # embedded interpreters don't have sys.argv, see bug #839151
-                filename = '__main__'
-        if not filename:
-            filename = module
     registry = globals.setdefault("__warningregistry__", {})
     warn_explicit(message, category, filename, lineno, module, registry,
                   globals, source)
diff --git a/Misc/NEWS.d/next/Library/2018-04-28-08-11-35.bpo-33375.Dbq1fz.rst b/Misc/NEWS.d/next/Library/2018-04-28-08-11-35.bpo-33375.Dbq1fz.rst
new file mode 100644
index 000000000000..f3f9d2a50881
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-04-28-08-11-35.bpo-33375.Dbq1fz.rst
@@ -0,0 +1,4 @@
+The warnings module now finds the Python file associated with a warning from
+the code object, rather than the frame's global namespace. This is
+consistent with how tracebacks and pdb find filenames, and should work
+better for dynamically executed code.
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 29e475d67d1f..9c7a6e17b3b8 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -671,7 +671,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
 {
     PyObject *globals;
 
-    /* Setup globals and lineno. */
+    /* Setup globals, filename and lineno. */
     PyFrameObject *f = PyThreadState_GET()->frame;
     // Stack level comparisons to Python code is off by one as there is no
     // warnings-related stack level to avoid.
@@ -688,10 +688,13 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
 
     if (f == NULL) {
         globals = PyThreadState_Get()->interp->sysdict;
+        *filename = PyUnicode_FromString("sys");
         *lineno = 1;
     }
     else {
         globals = f->f_globals;
+        *filename = f->f_code->co_filename;
+        Py_INCREF(*filename);
         *lineno = PyFrame_GetLineNumber(f);
     }
 
@@ -726,71 +729,6 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
             goto handle_error;
     }
 
-    /* Setup filename. */
-    *filename = PyDict_GetItemString(globals, "__file__");
-    if (*filename != NULL && PyUnicode_Check(*filename)) {
-        Py_ssize_t len;
-        int kind;
-        void *data;
-
-        if (PyUnicode_READY(*filename))
-            goto handle_error;
-
-        len = PyUnicode_GetLength(*filename);
-        kind = PyUnicode_KIND(*filename);
-        data = PyUnicode_DATA(*filename);
-
-#define ascii_lower(c) ((c <= 127) ? Py_TOLOWER(c) : 0)
-        /* if filename.lower().endswith(".pyc"): */
-        if (len >= 4 &&
-            PyUnicode_READ(kind, data, len-4) == '.' &&
-            ascii_lower(PyUnicode_READ(kind, data, len-3)) == 'p' &&
-            ascii_lower(PyUnicode_READ(kind, data, len-2)) == 'y' &&
-            ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'c')
-        {
-            *filename = PyUnicode_Substring(*filename, 0,
-                                            PyUnicode_GET_LENGTH(*filename)-1);
-            if (*filename == NULL)
-                goto handle_error;
-        }
-        else
-            Py_INCREF(*filename);
-    }
-    else {
-        *filename = NULL;
-        if (*module != Py_None && _PyUnicode_EqualToASCIIString(*module, "__main__")) {
-            PyObject *argv = _PySys_GetObjectId(&PyId_argv);
-            /* PyList_Check() is needed because sys.argv is set to None during
-               Python finalization */
-            if (argv != NULL && PyList_Check(argv) && PyList_Size(argv) > 0) {
-                int is_true;
-                *filename = PyList_GetItem(argv, 0);
-                Py_INCREF(*filename);
-                /* If sys.argv[0] is false, then use '__main__'. */
-                is_true = PyObject_IsTrue(*filename);
-                if (is_true < 0) {
-                    Py_DECREF(*filename);
-                    goto handle_error;
-                }
-                else if (!is_true) {
-                    Py_SETREF(*filename, PyUnicode_FromString("__main__"));
-                    if (*filename == NULL)
-                        goto handle_error;
-                }
-            }
-            else {
-                /* embedded interpreters don't have sys.argv, see bug #839151 */
-                *filename = PyUnicode_FromString("__main__");
-                if (*filename == NULL)
-                    goto handle_error;
-            }
-        }
-        if (*filename == NULL) {
-            *filename = *module;
-            Py_INCREF(*filename);
-        }
-    }
-
     return 1;
 
  handle_error:



More information about the Python-checkins mailing list