]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-135801: Improve filtering by module in warn_explicit() without module argument...
authorSerhiy Storchaka <storchaka@gmail.com>
Thu, 30 Oct 2025 13:55:39 +0000 (15:55 +0200)
committerGitHub <noreply@github.com>
Thu, 30 Oct 2025 13:55:39 +0000 (15:55 +0200)
* Try to match the module name pattern with module names constructed
  starting from different parent directories of the filename.
  E.g., for "/path/to/package/module" try to match with
  "path.to.package.module", "to.package.module", "package.module" and
  "module".
* Ignore trailing "/__init__.py".
* Ignore trailing ".pyw" on Windows.
* Keep matching with the full filename (without optional ".py" extension)
  for compatibility.
* Only ignore the case of the ".py" extension on Windows.

13 files changed:
Doc/library/warnings.rst
Doc/whatsnew/3.15.rst
Lib/_py_warnings.py
Lib/test/test_ast/test_ast.py
Lib/test/test_builtin.py
Lib/test/test_cmd_line_script.py
Lib/test/test_compile.py
Lib/test/test_import/__init__.py
Lib/test/test_import/data/syntax_warnings.py [new file with mode: 0644]
Lib/test/test_symtable.py
Lib/test/test_warnings/__init__.py
Misc/NEWS.d/next/Library/2025-10-16-17-17-20.gh-issue-135801.faH3fa.rst [new file with mode: 0644]
Python/_warnings.c

index 03b7a8dc378ef3cfe6f24d394bca0df0fc349cf8..2f3cf6008f58e2ec93f8b5d62d43e6c947cadf2f 100644 (file)
@@ -487,7 +487,14 @@ Available Functions
    ignored.
 
    *module*, if supplied, should be the module name.
-   If no module is passed, the filename with ``.py`` stripped is used.
+   If no module is passed, the module regular expression in
+   :ref:`warnings filter <warning-filter>` will be tested against the module
+   names constructed from the path components starting from all parent
+   directories (with ``/__init__.py``, ``.py`` and, on Windows, ``.pyw``
+   stripped) and against the filename with ``.py`` stripped.
+   For example, when the filename is ``'/path/to/package/module.py'``, it will
+   be tested against  ``'path.to.package.module'``, ``'to.package.module'``
+   ``'package.module'``, ``'module'``, and ``'/path/to/package/module'``.
 
    *registry*, if supplied, should be the ``__warningregistry__`` dictionary
    of the module.
@@ -506,6 +513,10 @@ Available Functions
    .. versionchanged:: 3.6
       Add the *source* parameter.
 
+   .. versionchanged:: next
+      If no module is passed, test the filter regular expression against
+      module names created from the path, not only the path itself.
+
 
 .. function:: showwarning(message, category, filename, lineno, file=None, line=None)
 
index fe9adfe9f730ecc1aba479fdb32bd78d7994cbca..903645fb40bb14e81eab1e74605364ea2e302c0c 100644 (file)
@@ -611,6 +611,18 @@ unittest
   (Contributed by Garry Cairns in :gh:`134567`.)
 
 
+warnings
+--------
+
+* Improve filtering by module in :func:`warnings.warn_explicit` if no *module*
+  argument is passed.
+  It now tests the module regular expression in the warnings filter not only
+  against the filename with ``.py`` stripped, but also against module names
+  constructed starting from different parent directories of the filename
+  (with ``/__init__.py``, ``.py`` and, on Windows, ``.pyw`` stripped).
+  (Contributed by Serhiy Storchaka in :gh:`135801`.)
+
+
 venv
 ----
 
index 576a17ea7b85013997cca3af9f6d636c8bfa1a89..91a9f44b20173371e9efa7125e57a027f7d0893e 100644 (file)
@@ -520,20 +520,50 @@ def warn(message, category=None, stacklevel=1, source=None,
     )
 
 
+def _match_filename(pattern, filename, *, MS_WINDOWS=(sys.platform == 'win32')):
+    if not filename:
+        return pattern.match('<unknown>') is not None
+    if filename[0] == '<' and filename[-1] == '>':
+        return pattern.match(filename) is not None
+
+    is_py = (filename[-3:].lower() == '.py'
+             if MS_WINDOWS else
+             filename.endswith('.py'))
+    if is_py:
+        filename = filename[:-3]
+    if pattern.match(filename):  # for backward compatibility
+        return True
+    if MS_WINDOWS:
+        if not is_py and filename[-4:].lower() == '.pyw':
+            filename = filename[:-4]
+            is_py = True
+        if is_py and filename[-9:].lower() in (r'\__init__', '/__init__'):
+            filename = filename[:-9]
+        filename = filename.replace('\\', '/')
+    else:
+        if is_py and filename.endswith('/__init__'):
+            filename = filename[:-9]
+    filename = filename.replace('/', '.')
+    i = 0
+    while True:
+        if pattern.match(filename, i):
+            return True
+        i = filename.find('.', i) + 1
+        if not i:
+            return False
+
+
 def warn_explicit(message, category, filename, lineno,
                   module=None, registry=None, module_globals=None,
                   source=None):
     lineno = int(lineno)
-    if module is None:
-        module = filename or "<unknown>"
-        if module[-3:].lower() == ".py":
-            module = module[:-3] # XXX What about leading pathname?
     if isinstance(message, Warning):
         text = str(message)
         category = message.__class__
     else:
         text = message
         message = category(message)
+    modules = None
     key = (text, category, lineno)
     with _wm._lock:
         if registry is None:
@@ -549,9 +579,11 @@ def warn_explicit(message, category, filename, lineno,
             action, msg, cat, mod, ln = item
             if ((msg is None or msg.match(text)) and
                 issubclass(category, cat) and
-                (mod is None or mod.match(module)) and
-                (ln == 0 or lineno == ln)):
-                break
+                (ln == 0 or lineno == ln) and
+                (mod is None or (_match_filename(mod, filename)
+                                 if module is None else
+                                 mod.match(module)))):
+                    break
         else:
             action = _wm.defaultaction
         # Early exit actions
index a979a4b1da1ad18ffb2e8d6595cdd1d3d128d6ba..551de5851daace1fd79bc55fce1c262ee1988198 100644 (file)
@@ -13,6 +13,7 @@ import tempfile
 import textwrap
 import types
 import unittest
+import warnings
 import weakref
 from io import StringIO
 from pathlib import Path
@@ -1069,6 +1070,19 @@ class AST_Tests(unittest.TestCase):
         self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
         self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation)
 
+    def test_filter_syntax_warnings_by_module(self):
+        filename = support.findfile('test_import/data/syntax_warnings.py')
+        with open(filename, 'rb') as f:
+            source = f.read()
+        with warnings.catch_warnings(record=True) as wlog:
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=r'<unknown>\z')
+            ast.parse(source)
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10])
+        for wm in wlog:
+            self.assertEqual(wm.filename, '<unknown>')
+            self.assertIs(wm.category, SyntaxWarning)
+
 
 class CopyTests(unittest.TestCase):
     """Test copying and pickling AST nodes."""
index fe3e391a7f5ba14e174d4f18c4596fa3d1c08a7d..fba46af6617640944d2bdad020dfcf3948dc3d89 100644 (file)
@@ -1088,6 +1088,28 @@ class BuiltinTest(ComplexesAreIdenticalMixin, unittest.TestCase):
             three_freevars.__globals__,
             closure=my_closure)
 
+    def test_exec_filter_syntax_warnings_by_module(self):
+        filename = support.findfile('test_import/data/syntax_warnings.py')
+        with open(filename, 'rb') as f:
+            source = f.read()
+        with warnings.catch_warnings(record=True) as wlog:
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=r'<string>\z')
+            exec(source, {})
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
+        for wm in wlog:
+            self.assertEqual(wm.filename, '<string>')
+            self.assertIs(wm.category, SyntaxWarning)
+
+        with warnings.catch_warnings(record=True) as wlog:
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=r'<string>\z')
+            exec(source, {'__name__': 'package.module', '__file__': filename})
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
+        for wm in wlog:
+            self.assertEqual(wm.filename, '<string>')
+            self.assertIs(wm.category, SyntaxWarning)
+
 
     def test_filter(self):
         self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld'))
index 784c45aa96f8a7a20f92c2c2ebc18aa0324325d7..f8115cc8300df74b8cf4dc3a4d66ddf9b78c3be0 100644 (file)
@@ -810,6 +810,19 @@ class CmdLineTest(unittest.TestCase):
                 out, err = p.communicate()
                 self.assertEqual(out, b"12345678912345678912345\n")
 
+    def test_filter_syntax_warnings_by_module(self):
+        filename = support.findfile('test_import/data/syntax_warnings.py')
+        rc, out, err = assert_python_ok(
+            '-Werror',
+            '-Walways:::test.test_import.data.syntax_warnings',
+            filename)
+        self.assertEqual(err.count(b': SyntaxWarning: '), 6)
+
+        rc, out, err = assert_python_ok(
+            '-Werror',
+            '-Walways:::syntax_warnings',
+            filename)
+        self.assertEqual(err.count(b': SyntaxWarning: '), 6)
 
 
 def tearDownModule():
index 846d38ae561fc54f1d38191ce3c89a6586aa7f46..9c2364491fe08d9ba48ab4ecee4a13ef0d574058 100644 (file)
@@ -1745,6 +1745,20 @@ class TestSpecifics(unittest.TestCase):
             self.assertEqual(wm.category, SyntaxWarning)
             self.assertIn("\"is\" with 'int' literal", str(wm.message))
 
+    def test_filter_syntax_warnings_by_module(self):
+        filename = support.findfile('test_import/data/syntax_warnings.py')
+        with open(filename, 'rb') as f:
+            source = f.read()
+        module_re = r'test\.test_import\.data\.syntax_warnings\z'
+        with warnings.catch_warnings(record=True) as wlog:
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=module_re)
+            compile(source, filename, 'exec')
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
+        for wm in wlog:
+            self.assertEqual(wm.filename, filename)
+            self.assertIs(wm.category, SyntaxWarning)
+
     @support.subTests('src', [
         textwrap.dedent("""
             def f():
index 95121debbbfa74e9d69f6a34e95e41004ec6b414..072021e595975a7ebd4c85ac025266f113cc47fb 100644 (file)
@@ -15,6 +15,7 @@ import marshal
 import os
 import py_compile
 import random
+import re
 import shutil
 import stat
 import subprocess
@@ -23,6 +24,7 @@ import textwrap
 import threading
 import time
 import types
+import warnings
 import unittest
 from unittest import mock
 import _imp
@@ -51,7 +53,7 @@ from test.support.os_helper import (
     TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE)
 from test.support import script_helper
 from test.support import threading_helper
-from test.test_importlib.util import uncache
+from test.test_importlib.util import uncache, temporary_pycache_prefix
 from types import ModuleType
 try:
     import _testsinglephase
@@ -412,7 +414,6 @@ class ImportTests(unittest.TestCase):
         self.assertIsNotNone(cm.exception)
 
     def test_from_import_star_invalid_type(self):
-        import re
         with ready_to_import() as (name, path):
             with open(path, 'w', encoding='utf-8') as f:
                 f.write("__all__ = [b'invalid_type']")
@@ -1250,6 +1251,35 @@ os.does_not_exist
                 origin = "a\x00b"
             _imp.create_dynamic(Spec2())
 
+    def test_filter_syntax_warnings_by_module(self):
+        module_re = r'test\.test_import\.data\.syntax_warnings\z'
+        unload('test.test_import.data.syntax_warnings')
+        with (os_helper.temp_dir() as tmpdir,
+              temporary_pycache_prefix(tmpdir),
+              warnings.catch_warnings(record=True) as wlog):
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=module_re)
+            import test.test_import.data.syntax_warnings
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
+        filename = test.test_import.data.syntax_warnings.__file__
+        for wm in wlog:
+            self.assertEqual(wm.filename, filename)
+            self.assertIs(wm.category, SyntaxWarning)
+
+        module_re = r'syntax_warnings\z'
+        unload('test.test_import.data.syntax_warnings')
+        with (os_helper.temp_dir() as tmpdir,
+              temporary_pycache_prefix(tmpdir),
+              warnings.catch_warnings(record=True) as wlog):
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=module_re)
+            import test.test_import.data.syntax_warnings
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10, 13, 14, 21])
+        filename = test.test_import.data.syntax_warnings.__file__
+        for wm in wlog:
+            self.assertEqual(wm.filename, filename)
+            self.assertIs(wm.category, SyntaxWarning)
+
 
 @skip_if_dont_write_bytecode
 class FilePermissionTests(unittest.TestCase):
diff --git a/Lib/test/test_import/data/syntax_warnings.py b/Lib/test/test_import/data/syntax_warnings.py
new file mode 100644 (file)
index 0000000..103f07b
--- /dev/null
@@ -0,0 +1,21 @@
+# Syntax warnings emitted in different parts of the Python compiler.
+
+# Parser/lexer/lexer.c
+x = 1or 0  # line 4
+
+# Parser/tokenizer/helpers.c
+'\z'  # line 7
+
+# Parser/string_parser.c
+'\400'  # line 10
+
+# _PyCompile_Warn() in Python/codegen.c
+assert(x, 'message')  # line 13
+x is 1  # line 14
+
+# _PyErr_EmitSyntaxWarning() in Python/ast_preprocess.c
+def f():
+    try:
+        pass
+    finally:
+        return 42  # line 21
index 943e63fc13c921a88789f7a59e07578cd152aa4a..ef2c00e04b820c87490ac33ce1da00e07a7110e1 100644 (file)
@@ -5,6 +5,7 @@ Test the API of the symtable module.
 import re
 import textwrap
 import symtable
+import warnings
 import unittest
 
 from test import support
@@ -586,6 +587,20 @@ class SymtableTest(unittest.TestCase):
         # check error path when 'compile_type' AC conversion failed
         self.assertRaises(TypeError, symtable.symtable, '', mortal_str, 1)
 
+    def test_filter_syntax_warnings_by_module(self):
+        filename = support.findfile('test_import/data/syntax_warnings.py')
+        with open(filename, 'rb') as f:
+            source = f.read()
+        module_re = r'test\.test_import\.data\.syntax_warnings\z'
+        with warnings.catch_warnings(record=True) as wlog:
+            warnings.simplefilter('error')
+            warnings.filterwarnings('always', module=module_re)
+            symtable.symtable(source, filename, 'exec')
+        self.assertEqual(sorted(wm.lineno for wm in wlog), [4, 7, 10])
+        for wm in wlog:
+            self.assertEqual(wm.filename, filename)
+            self.assertIs(wm.category, SyntaxWarning)
+
 
 class ComprehensionTests(unittest.TestCase):
     def get_identifiers_recursive(self, st, res):
index 157852cfa910078f88a3d12b6a9fc239c51b2d59..e6666ddc638037c5cb903349a33d83c4ed15ade0 100644 (file)
@@ -249,10 +249,23 @@ class FilterTests(BaseTest):
             self.module.warn_explicit('msg', UserWarning, 'filename', 42,
                                       module='package.module')
             self.assertEqual(len(w), 1)
+            self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
+            self.assertEqual(len(w), 2)
+            self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
+            self.assertEqual(len(w), 3)
+            self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__.py', 42)
+            self.assertEqual(len(w), 4)
             with self.assertRaises(UserWarning):
-                self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
-            with self.assertRaises(UserWarning):
-                self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
+                self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__', 42)
+            if MS_WINDOWS:
+                self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
+                self.assertEqual(len(w), 5)
+                self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PY', 42)
+                self.assertEqual(len(w), 6)
+                self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PYW', 42)
+                self.assertEqual(len(w), 7)
+                self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module\__INIT__.PYW', 42)
+                self.assertEqual(len(w), 8)
 
         with self.module.catch_warnings(record=True) as w:
             self.module.simplefilter('error')
@@ -276,9 +289,8 @@ class FilterTests(BaseTest):
             with self.assertRaises(UserWarning):
                 self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42)
             if MS_WINDOWS:
-                if self.module is py_warnings:
-                    self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
-                    self.assertEqual(len(w), 3)
+                self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
+                self.assertEqual(len(w), 3)
                 with self.assertRaises(UserWarning):
                     self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42)
                 with self.assertRaises(UserWarning):
@@ -302,9 +314,8 @@ class FilterTests(BaseTest):
                 self.assertEqual(len(w), 1)
                 self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42)
                 self.assertEqual(len(w), 2)
-                if self.module is py_warnings:
-                    self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
-                    self.assertEqual(len(w), 3)
+                self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
+                self.assertEqual(len(w), 3)
                 with self.assertRaises(UserWarning):
                     self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42)
                 with self.assertRaises(UserWarning):
@@ -399,7 +410,7 @@ class FilterTests(BaseTest):
 
     def test_mutate_filter_list(self):
         class X:
-            def match(self, a):
+            def match(self, a, start=0):
                 L[:] = []
 
         L = [("default",X(),UserWarning,X(),0) for i in range(2)]
diff --git a/Misc/NEWS.d/next/Library/2025-10-16-17-17-20.gh-issue-135801.faH3fa.rst b/Misc/NEWS.d/next/Library/2025-10-16-17-17-20.gh-issue-135801.faH3fa.rst
new file mode 100644 (file)
index 0000000..d680312
--- /dev/null
@@ -0,0 +1,6 @@
+Improve filtering by module in :func:`warnings.warn_explicit` if no *module*
+argument is passed. It now tests the module regular expression in the
+warnings filter not only against the filename with ``.py`` stripped, but
+also against module names constructed starting from different parent
+directories of the filename (with ``/__init__.py``, ``.py`` and, on Windows,
+``.pyw`` stripped).
index 9989b623dbce3a29b3b2bb8498a168c3fa7f5470..d44d414bc93a04dbb6d19f3598d05a27f453a1b7 100644 (file)
@@ -171,7 +171,7 @@ _PyWarnings_InitState(PyInterpreterState *interp)
 /*************************************************************************/
 
 static int
-check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
+check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg, PyObject *arg2)
 {
     PyObject *result;
     int rc;
@@ -182,6 +182,9 @@ check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
 
     /* An internal plain text default filter must match exactly */
     if (PyUnicode_CheckExact(obj)) {
+        if (arg == NULL) {
+            return 0;
+        }
         int cmp_result = PyUnicode_Compare(obj, arg);
         if (cmp_result == -1 && PyErr_Occurred()) {
             return -1;
@@ -190,10 +193,19 @@ check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
     }
 
     /* Otherwise assume a regex filter and call its match() method */
-    result = PyObject_CallMethodOneArg(obj, &_Py_ID(match), arg);
+    if (arg != NULL) {
+        result = PyObject_CallMethodOneArg(obj, &_Py_ID(match), arg);
+    }
+    else {
+        PyObject *match = PyImport_ImportModuleAttrString("_py_warnings", "_match_filename");
+        if (match == NULL) {
+            return -1;
+        }
+        result = PyObject_CallFunctionObjArgs(match, obj, arg2, NULL);
+        Py_DECREF(match);
+    }
     if (result == NULL)
         return -1;
-
     rc = PyObject_IsTrue(result);
     Py_DECREF(result);
     return rc;
@@ -423,7 +435,7 @@ get_default_action(PyInterpreterState *interp)
 static bool
 filter_search(PyInterpreterState *interp, PyObject *category,
               PyObject *text, Py_ssize_t lineno,
-              PyObject *module, char *list_name, PyObject *filters,
+              PyObject *module, PyObject *filename, char *list_name, PyObject *filters,
               PyObject **item, PyObject **matched_action) {
     bool result = true;
     *matched_action = NULL;
@@ -459,14 +471,14 @@ filter_search(PyInterpreterState *interp, PyObject *category,
             break;
         }
 
-        good_msg = check_matched(interp, msg, text);
+        good_msg = check_matched(interp, msg, text, NULL);
         if (good_msg == -1) {
             Py_DECREF(tmp_item);
             result = false;
             break;
         }
 
-        good_mod = check_matched(interp, mod, module);
+        good_mod = check_matched(interp, mod, module, filename);
         if (good_mod == -1) {
             Py_DECREF(tmp_item);
             result = false;
@@ -504,7 +516,7 @@ filter_search(PyInterpreterState *interp, PyObject *category,
 static PyObject*
 get_filter(PyInterpreterState *interp, PyObject *category,
            PyObject *text, Py_ssize_t lineno,
-           PyObject *module, PyObject **item)
+           PyObject *module, PyObject *filename, PyObject **item)
 {
 #ifdef Py_DEBUG
     WarningsState *st = warnings_get_state(interp);
@@ -522,7 +534,7 @@ get_filter(PyInterpreterState *interp, PyObject *category,
         use_global_filters = true;
     } else {
         PyObject *context_action = NULL;
-        if (!filter_search(interp, category, text, lineno, module, "_warnings_context _filters",
+        if (!filter_search(interp, category, text, lineno, module, filename, "_warnings_context _filters",
                            context_filters, item, &context_action)) {
             Py_DECREF(context_filters);
             return NULL;
@@ -541,7 +553,7 @@ get_filter(PyInterpreterState *interp, PyObject *category,
         if (filters == NULL) {
             return NULL;
         }
-        if (!filter_search(interp, category, text, lineno, module, "filters",
+        if (!filter_search(interp, category, text, lineno, module, filename, "filters",
                            filters, item, &action)) {
             return NULL;
         }
@@ -612,39 +624,6 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key,
     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 = Py_NewRef(filename);
-    }
-    return module;
-}
-
 static int
 update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text,
                 PyObject *category, int add_zero)
@@ -812,15 +791,6 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message,
         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. */
     if (PyObject_TypeCheck(message, (PyTypeObject *)PyExc_Warning)) {
@@ -858,7 +828,7 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message,
         /* Else this warning hasn't been generated before. */
     }
 
-    action = get_filter(interp, category, text, lineno, module, &item);
+    action = get_filter(interp, category, text, lineno, module, filename, &item);
     if (action == NULL)
         goto cleanup;
 
@@ -921,7 +891,6 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message,
     Py_XDECREF(key);
     Py_XDECREF(text);
     Py_XDECREF(lineno_obj);
-    Py_DECREF(module);
     Py_XDECREF(message);
     return result;  /* Py_None or NULL. */
 }