Skip to content

Commit 11a8966

Browse files
takluyverbrettcannon
authored andcommitted
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).
1 parent 3b0b90c commit 11a8966

File tree

4 files changed

+19
-155
lines changed

4 files changed

+19
-155
lines changed

Lib/test/test_warnings/__init__.py

Lines changed: 9 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -441,78 +441,14 @@ def test_stacklevel_import(self):
441441
self.assertEqual(len(w), 1)
442442
self.assertEqual(w[0].filename, __file__)
443443

444-
def test_missing_filename_not_main(self):
445-
# If __file__ is not specified and __main__ is not the module name,
446-
# then __file__ should be set to the module name.
447-
filename = warning_tests.__file__
448-
try:
449-
del warning_tests.__file__
450-
with warnings_state(self.module):
451-
with original_warnings.catch_warnings(record=True,
452-
module=self.module) as w:
453-
warning_tests.inner("spam8", stacklevel=1)
454-
self.assertEqual(w[-1].filename, warning_tests.__name__)
455-
finally:
456-
warning_tests.__file__ = filename
457-
458-
@unittest.skipUnless(hasattr(sys, 'argv'), 'test needs sys.argv')
459-
def test_missing_filename_main_with_argv(self):
460-
# If __file__ is not specified and the caller is __main__ and sys.argv
461-
# exists, then use sys.argv[0] as the file.
462-
filename = warning_tests.__file__
463-
module_name = warning_tests.__name__
464-
try:
465-
del warning_tests.__file__
466-
warning_tests.__name__ = '__main__'
467-
with warnings_state(self.module):
468-
with original_warnings.catch_warnings(record=True,
469-
module=self.module) as w:
470-
warning_tests.inner('spam9', stacklevel=1)
471-
self.assertEqual(w[-1].filename, sys.argv[0])
472-
finally:
473-
warning_tests.__file__ = filename
474-
warning_tests.__name__ = module_name
475-
476-
def test_missing_filename_main_without_argv(self):
477-
# If __file__ is not specified, the caller is __main__, and sys.argv
478-
# is not set, then '__main__' is the file name.
479-
filename = warning_tests.__file__
480-
module_name = warning_tests.__name__
481-
argv = sys.argv
482-
try:
483-
del warning_tests.__file__
484-
warning_tests.__name__ = '__main__'
485-
del sys.argv
486-
with warnings_state(self.module):
487-
with original_warnings.catch_warnings(record=True,
488-
module=self.module) as w:
489-
warning_tests.inner('spam10', stacklevel=1)
490-
self.assertEqual(w[-1].filename, '__main__')
491-
finally:
492-
warning_tests.__file__ = filename
493-
warning_tests.__name__ = module_name
494-
sys.argv = argv
495-
496-
def test_missing_filename_main_with_argv_empty_string(self):
497-
# If __file__ is not specified, the caller is __main__, and sys.argv[0]
498-
# is the empty string, then '__main__ is the file name.
499-
# Tests issue 2743.
500-
file_name = warning_tests.__file__
501-
module_name = warning_tests.__name__
502-
argv = sys.argv
503-
try:
504-
del warning_tests.__file__
505-
warning_tests.__name__ = '__main__'
506-
sys.argv = ['']
507-
with warnings_state(self.module):
508-
with original_warnings.catch_warnings(record=True,
509-
module=self.module) as w:
510-
warning_tests.inner('spam11', stacklevel=1)
511-
self.assertEqual(w[-1].filename, '__main__')
512-
finally:
513-
warning_tests.__file__ = file_name
514-
warning_tests.__name__ = module_name
515-
sys.argv = argv
444+
def test_exec_filename(self):
445+
filename = "<warnings-test>"
446+
codeobj = compile(("import warnings\n"
447+
"warnings.warn('hello', UserWarning)"),
448+
filename, "exec")
449+
with original_warnings.catch_warnings(record=True) as w:
450+
exec(codeobj)
451+
self.assertEqual(w[0].filename, filename)
516452

517453
def test_warn_explicit_non_ascii_filename(self):
518454
with original_warnings.catch_warnings(record=True,
@@ -1245,9 +1181,7 @@ def __del__(self):
12451181
a=A()
12461182
"""
12471183
rc, out, err = assert_python_ok("-c", code)
1248-
# note: "__main__" filename is not correct, it should be the name
1249-
# of the script
1250-
self.assertEqual(err.decode(), '__main__:7: UserWarning: test')
1184+
self.assertEqual(err.decode(), '<string>:7: UserWarning: test')
12511185

12521186
def test_late_resource_warning(self):
12531187
# Issue #21925: Emitting a ResourceWarning late during the Python

Lib/warnings.py

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -303,28 +303,16 @@ def warn(message, category=None, stacklevel=1, source=None):
303303
raise ValueError
304304
except ValueError:
305305
globals = sys.__dict__
306+
filename = "sys"
306307
lineno = 1
307308
else:
308309
globals = frame.f_globals
310+
filename = frame.f_code.co_filename
309311
lineno = frame.f_lineno
310312
if '__name__' in globals:
311313
module = globals['__name__']
312314
else:
313315
module = "<string>"
314-
filename = globals.get('__file__')
315-
if filename:
316-
fnl = filename.lower()
317-
if fnl.endswith(".pyc"):
318-
filename = filename[:-1]
319-
else:
320-
if module == "__main__":
321-
try:
322-
filename = sys.argv[0]
323-
except AttributeError:
324-
# embedded interpreters don't have sys.argv, see bug #839151
325-
filename = '__main__'
326-
if not filename:
327-
filename = module
328316
registry = globals.setdefault("__warningregistry__", {})
329317
warn_explicit(message, category, filename, lineno, module, registry,
330318
globals, source)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
The warnings module now finds the Python file associated with a warning from
2+
the code object, rather than the frame's global namespace. This is
3+
consistent with how tracebacks and pdb find filenames, and should work
4+
better for dynamically executed code.

Python/_warnings.c

Lines changed: 4 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
671671
{
672672
PyObject *globals;
673673

674-
/* Setup globals and lineno. */
674+
/* Setup globals, filename and lineno. */
675675
PyFrameObject *f = PyThreadState_GET()->frame;
676676
// Stack level comparisons to Python code is off by one as there is no
677677
// warnings-related stack level to avoid.
@@ -688,10 +688,13 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
688688

689689
if (f == NULL) {
690690
globals = PyThreadState_Get()->interp->sysdict;
691+
*filename = PyUnicode_FromString("sys");
691692
*lineno = 1;
692693
}
693694
else {
694695
globals = f->f_globals;
696+
*filename = f->f_code->co_filename;
697+
Py_INCREF(*filename);
695698
*lineno = PyFrame_GetLineNumber(f);
696699
}
697700

@@ -726,71 +729,6 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
726729
goto handle_error;
727730
}
728731

729-
/* Setup filename. */
730-
*filename = PyDict_GetItemString(globals, "__file__");
731-
if (*filename != NULL && PyUnicode_Check(*filename)) {
732-
Py_ssize_t len;
733-
int kind;
734-
void *data;
735-
736-
if (PyUnicode_READY(*filename))
737-
goto handle_error;
738-
739-
len = PyUnicode_GetLength(*filename);
740-
kind = PyUnicode_KIND(*filename);
741-
data = PyUnicode_DATA(*filename);
742-
743-
#define ascii_lower(c) ((c <= 127) ? Py_TOLOWER(c) : 0)
744-
/* if filename.lower().endswith(".pyc"): */
745-
if (len >= 4 &&
746-
PyUnicode_READ(kind, data, len-4) == '.' &&
747-
ascii_lower(PyUnicode_READ(kind, data, len-3)) == 'p' &&
748-
ascii_lower(PyUnicode_READ(kind, data, len-2)) == 'y' &&
749-
ascii_lower(PyUnicode_READ(kind, data, len-1)) == 'c')
750-
{
751-
*filename = PyUnicode_Substring(*filename, 0,
752-
PyUnicode_GET_LENGTH(*filename)-1);
753-
if (*filename == NULL)
754-
goto handle_error;
755-
}
756-
else
757-
Py_INCREF(*filename);
758-
}
759-
else {
760-
*filename = NULL;
761-
if (*module != Py_None && _PyUnicode_EqualToASCIIString(*module, "__main__")) {
762-
PyObject *argv = _PySys_GetObjectId(&PyId_argv);
763-
/* PyList_Check() is needed because sys.argv is set to None during
764-
Python finalization */
765-
if (argv != NULL && PyList_Check(argv) && PyList_Size(argv) > 0) {
766-
int is_true;
767-
*filename = PyList_GetItem(argv, 0);
768-
Py_INCREF(*filename);
769-
/* If sys.argv[0] is false, then use '__main__'. */
770-
is_true = PyObject_IsTrue(*filename);
771-
if (is_true < 0) {
772-
Py_DECREF(*filename);
773-
goto handle_error;
774-
}
775-
else if (!is_true) {
776-
Py_SETREF(*filename, PyUnicode_FromString("__main__"));
777-
if (*filename == NULL)
778-
goto handle_error;
779-
}
780-
}
781-
else {
782-
/* embedded interpreters don't have sys.argv, see bug #839151 */
783-
*filename = PyUnicode_FromString("__main__");
784-
if (*filename == NULL)
785-
goto handle_error;
786-
}
787-
}
788-
if (*filename == NULL) {
789-
*filename = *module;
790-
Py_INCREF(*filename);
791-
}
792-
}
793-
794732
return 1;
795733

796734
handle_error:

0 commit comments

Comments
 (0)