mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
gh-135801: Improve filtering by module in warn_explicit() without module argument (GH-140151)
* 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.
This commit is contained in:
parent
efc37ba49e
commit
6826166280
13 changed files with 243 additions and 73 deletions
|
|
@ -487,7 +487,14 @@ Available Functions
|
||||||
ignored.
|
ignored.
|
||||||
|
|
||||||
*module*, if supplied, should be the module name.
|
*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
|
*registry*, if supplied, should be the ``__warningregistry__`` dictionary
|
||||||
of the module.
|
of the module.
|
||||||
|
|
@ -506,6 +513,10 @@ Available Functions
|
||||||
.. versionchanged:: 3.6
|
.. versionchanged:: 3.6
|
||||||
Add the *source* parameter.
|
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)
|
.. function:: showwarning(message, category, filename, lineno, file=None, line=None)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -611,6 +611,18 @@ unittest
|
||||||
(Contributed by Garry Cairns in :gh:`134567`.)
|
(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
|
venv
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
def warn_explicit(message, category, filename, lineno,
|
||||||
module=None, registry=None, module_globals=None,
|
module=None, registry=None, module_globals=None,
|
||||||
source=None):
|
source=None):
|
||||||
lineno = int(lineno)
|
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):
|
if isinstance(message, Warning):
|
||||||
text = str(message)
|
text = str(message)
|
||||||
category = message.__class__
|
category = message.__class__
|
||||||
else:
|
else:
|
||||||
text = message
|
text = message
|
||||||
message = category(message)
|
message = category(message)
|
||||||
|
modules = None
|
||||||
key = (text, category, lineno)
|
key = (text, category, lineno)
|
||||||
with _wm._lock:
|
with _wm._lock:
|
||||||
if registry is None:
|
if registry is None:
|
||||||
|
|
@ -549,9 +579,11 @@ def warn_explicit(message, category, filename, lineno,
|
||||||
action, msg, cat, mod, ln = item
|
action, msg, cat, mod, ln = item
|
||||||
if ((msg is None or msg.match(text)) and
|
if ((msg is None or msg.match(text)) and
|
||||||
issubclass(category, cat) and
|
issubclass(category, cat) and
|
||||||
(mod is None or mod.match(module)) and
|
(ln == 0 or lineno == ln) and
|
||||||
(ln == 0 or lineno == ln)):
|
(mod is None or (_match_filename(mod, filename)
|
||||||
break
|
if module is None else
|
||||||
|
mod.match(module)))):
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
action = _wm.defaultaction
|
action = _wm.defaultaction
|
||||||
# Early exit actions
|
# Early exit actions
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
import textwrap
|
import textwrap
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
|
import warnings
|
||||||
import weakref
|
import weakref
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -1069,6 +1070,19 @@ def test_tstring(self):
|
||||||
self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
|
self.assertIsInstance(tree.body[0].value.values[0], ast.Constant)
|
||||||
self.assertIsInstance(tree.body[0].value.values[1], ast.Interpolation)
|
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):
|
class CopyTests(unittest.TestCase):
|
||||||
"""Test copying and pickling AST nodes."""
|
"""Test copying and pickling AST nodes."""
|
||||||
|
|
|
||||||
|
|
@ -1088,6 +1088,28 @@ def four_freevars():
|
||||||
three_freevars.__globals__,
|
three_freevars.__globals__,
|
||||||
closure=my_closure)
|
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):
|
def test_filter(self):
|
||||||
self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld'))
|
self.assertEqual(list(filter(lambda c: 'a' <= c <= 'z', 'Hello World')), list('elloorld'))
|
||||||
|
|
|
||||||
|
|
@ -810,6 +810,19 @@ def test_script_as_dev_fd(self):
|
||||||
out, err = p.communicate()
|
out, err = p.communicate()
|
||||||
self.assertEqual(out, b"12345678912345678912345\n")
|
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():
|
def tearDownModule():
|
||||||
|
|
|
||||||
|
|
@ -1745,6 +1745,20 @@ def test_compile_warning_in_finally(self):
|
||||||
self.assertEqual(wm.category, SyntaxWarning)
|
self.assertEqual(wm.category, SyntaxWarning)
|
||||||
self.assertIn("\"is\" with 'int' literal", str(wm.message))
|
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', [
|
@support.subTests('src', [
|
||||||
textwrap.dedent("""
|
textwrap.dedent("""
|
||||||
def f():
|
def f():
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
import os
|
import os
|
||||||
import py_compile
|
import py_compile
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import stat
|
import stat
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
@ -23,6 +24,7 @@
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
|
import warnings
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
import _imp
|
import _imp
|
||||||
|
|
@ -51,7 +53,7 @@
|
||||||
TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE)
|
TESTFN, rmtree, temp_umask, TESTFN_UNENCODABLE)
|
||||||
from test.support import script_helper
|
from test.support import script_helper
|
||||||
from test.support import threading_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
|
from types import ModuleType
|
||||||
try:
|
try:
|
||||||
import _testsinglephase
|
import _testsinglephase
|
||||||
|
|
@ -412,7 +414,6 @@ def test_from_import_missing_attr_path_is_canonical(self):
|
||||||
self.assertIsNotNone(cm.exception)
|
self.assertIsNotNone(cm.exception)
|
||||||
|
|
||||||
def test_from_import_star_invalid_type(self):
|
def test_from_import_star_invalid_type(self):
|
||||||
import re
|
|
||||||
with ready_to_import() as (name, path):
|
with ready_to_import() as (name, path):
|
||||||
with open(path, 'w', encoding='utf-8') as f:
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
f.write("__all__ = [b'invalid_type']")
|
f.write("__all__ = [b'invalid_type']")
|
||||||
|
|
@ -1250,6 +1251,35 @@ class Spec2:
|
||||||
origin = "a\x00b"
|
origin = "a\x00b"
|
||||||
_imp.create_dynamic(Spec2())
|
_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
|
@skip_if_dont_write_bytecode
|
||||||
class FilePermissionTests(unittest.TestCase):
|
class FilePermissionTests(unittest.TestCase):
|
||||||
|
|
|
||||||
21
Lib/test/test_import/data/syntax_warnings.py
Normal file
21
Lib/test/test_import/data/syntax_warnings.py
Normal file
|
|
@ -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
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
import re
|
import re
|
||||||
import textwrap
|
import textwrap
|
||||||
import symtable
|
import symtable
|
||||||
|
import warnings
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
|
|
@ -586,6 +587,20 @@ def test__symtable_refleak(self):
|
||||||
# check error path when 'compile_type' AC conversion failed
|
# check error path when 'compile_type' AC conversion failed
|
||||||
self.assertRaises(TypeError, symtable.symtable, '', mortal_str, 1)
|
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):
|
class ComprehensionTests(unittest.TestCase):
|
||||||
def get_identifiers_recursive(self, st, res):
|
def get_identifiers_recursive(self, st, res):
|
||||||
|
|
|
||||||
|
|
@ -249,10 +249,23 @@ def test_filter_module(self):
|
||||||
self.module.warn_explicit('msg', UserWarning, 'filename', 42,
|
self.module.warn_explicit('msg', UserWarning, 'filename', 42,
|
||||||
module='package.module')
|
module='package.module')
|
||||||
self.assertEqual(len(w), 1)
|
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):
|
with self.assertRaises(UserWarning):
|
||||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module', 42)
|
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module/__init__', 42)
|
||||||
with self.assertRaises(UserWarning):
|
if MS_WINDOWS:
|
||||||
self.module.warn_explicit('msg', UserWarning, '/path/to/package/module.py', 42)
|
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:
|
with self.module.catch_warnings(record=True) as w:
|
||||||
self.module.simplefilter('error')
|
self.module.simplefilter('error')
|
||||||
|
|
@ -276,9 +289,8 @@ def test_filter_module(self):
|
||||||
with self.assertRaises(UserWarning):
|
with self.assertRaises(UserWarning):
|
||||||
self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42)
|
self.module.warn_explicit('msg', UserWarning, '/PATH/TO/PACKAGE/MODULE', 42)
|
||||||
if MS_WINDOWS:
|
if MS_WINDOWS:
|
||||||
if self.module is py_warnings:
|
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
|
||||||
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module.PY', 42)
|
self.assertEqual(len(w), 3)
|
||||||
self.assertEqual(len(w), 3)
|
|
||||||
with self.assertRaises(UserWarning):
|
with self.assertRaises(UserWarning):
|
||||||
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42)
|
self.module.warn_explicit('msg', UserWarning, r'/path/to/package/module/__init__.py', 42)
|
||||||
with self.assertRaises(UserWarning):
|
with self.assertRaises(UserWarning):
|
||||||
|
|
@ -302,9 +314,8 @@ def test_filter_module(self):
|
||||||
self.assertEqual(len(w), 1)
|
self.assertEqual(len(w), 1)
|
||||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42)
|
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.py', 42)
|
||||||
self.assertEqual(len(w), 2)
|
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.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.PY', 42)
|
self.assertEqual(len(w), 3)
|
||||||
self.assertEqual(len(w), 3)
|
|
||||||
with self.assertRaises(UserWarning):
|
with self.assertRaises(UserWarning):
|
||||||
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42)
|
self.module.warn_explicit('msg', UserWarning, r'C:\path\to\package\module.pyw', 42)
|
||||||
with self.assertRaises(UserWarning):
|
with self.assertRaises(UserWarning):
|
||||||
|
|
@ -399,7 +410,7 @@ def test_message_matching(self):
|
||||||
|
|
||||||
def test_mutate_filter_list(self):
|
def test_mutate_filter_list(self):
|
||||||
class X:
|
class X:
|
||||||
def match(self, a):
|
def match(self, a, start=0):
|
||||||
L[:] = []
|
L[:] = []
|
||||||
|
|
||||||
L = [("default",X(),UserWarning,X(),0) for i in range(2)]
|
L = [("default",X(),UserWarning,X(),0) for i in range(2)]
|
||||||
|
|
|
||||||
|
|
@ -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).
|
||||||
|
|
@ -171,7 +171,7 @@ _PyWarnings_InitState(PyInterpreterState *interp)
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
|
check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg, PyObject *arg2)
|
||||||
{
|
{
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
@ -182,6 +182,9 @@ check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
|
||||||
|
|
||||||
/* An internal plain text default filter must match exactly */
|
/* An internal plain text default filter must match exactly */
|
||||||
if (PyUnicode_CheckExact(obj)) {
|
if (PyUnicode_CheckExact(obj)) {
|
||||||
|
if (arg == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int cmp_result = PyUnicode_Compare(obj, arg);
|
int cmp_result = PyUnicode_Compare(obj, arg);
|
||||||
if (cmp_result == -1 && PyErr_Occurred()) {
|
if (cmp_result == -1 && PyErr_Occurred()) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -190,10 +193,19 @@ check_matched(PyInterpreterState *interp, PyObject *obj, PyObject *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Otherwise assume a regex filter and call its match() method */
|
/* 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)
|
if (result == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
rc = PyObject_IsTrue(result);
|
rc = PyObject_IsTrue(result);
|
||||||
Py_DECREF(result);
|
Py_DECREF(result);
|
||||||
return rc;
|
return rc;
|
||||||
|
|
@ -423,7 +435,7 @@ get_default_action(PyInterpreterState *interp)
|
||||||
static bool
|
static bool
|
||||||
filter_search(PyInterpreterState *interp, PyObject *category,
|
filter_search(PyInterpreterState *interp, PyObject *category,
|
||||||
PyObject *text, Py_ssize_t lineno,
|
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) {
|
PyObject **item, PyObject **matched_action) {
|
||||||
bool result = true;
|
bool result = true;
|
||||||
*matched_action = NULL;
|
*matched_action = NULL;
|
||||||
|
|
@ -459,14 +471,14 @@ filter_search(PyInterpreterState *interp, PyObject *category,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
good_msg = check_matched(interp, msg, text);
|
good_msg = check_matched(interp, msg, text, NULL);
|
||||||
if (good_msg == -1) {
|
if (good_msg == -1) {
|
||||||
Py_DECREF(tmp_item);
|
Py_DECREF(tmp_item);
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
good_mod = check_matched(interp, mod, module);
|
good_mod = check_matched(interp, mod, module, filename);
|
||||||
if (good_mod == -1) {
|
if (good_mod == -1) {
|
||||||
Py_DECREF(tmp_item);
|
Py_DECREF(tmp_item);
|
||||||
result = false;
|
result = false;
|
||||||
|
|
@ -504,7 +516,7 @@ filter_search(PyInterpreterState *interp, PyObject *category,
|
||||||
static PyObject*
|
static PyObject*
|
||||||
get_filter(PyInterpreterState *interp, PyObject *category,
|
get_filter(PyInterpreterState *interp, PyObject *category,
|
||||||
PyObject *text, Py_ssize_t lineno,
|
PyObject *text, Py_ssize_t lineno,
|
||||||
PyObject *module, PyObject **item)
|
PyObject *module, PyObject *filename, PyObject **item)
|
||||||
{
|
{
|
||||||
#ifdef Py_DEBUG
|
#ifdef Py_DEBUG
|
||||||
WarningsState *st = warnings_get_state(interp);
|
WarningsState *st = warnings_get_state(interp);
|
||||||
|
|
@ -522,7 +534,7 @@ get_filter(PyInterpreterState *interp, PyObject *category,
|
||||||
use_global_filters = true;
|
use_global_filters = true;
|
||||||
} else {
|
} else {
|
||||||
PyObject *context_action = NULL;
|
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)) {
|
context_filters, item, &context_action)) {
|
||||||
Py_DECREF(context_filters);
|
Py_DECREF(context_filters);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -541,7 +553,7 @@ get_filter(PyInterpreterState *interp, PyObject *category,
|
||||||
if (filters == NULL) {
|
if (filters == NULL) {
|
||||||
return 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)) {
|
filters, item, &action)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -612,39 +624,6 @@ already_warned(PyInterpreterState *interp, PyObject *registry, PyObject *key,
|
||||||
return 0;
|
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
|
static int
|
||||||
update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text,
|
update_registry(PyInterpreterState *interp, PyObject *registry, PyObject *text,
|
||||||
PyObject *category, int add_zero)
|
PyObject *category, int add_zero)
|
||||||
|
|
@ -812,15 +791,6 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Normalize module. */
|
|
||||||
if (module == NULL) {
|
|
||||||
module = normalize_module(filename);
|
|
||||||
if (module == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Py_INCREF(module);
|
|
||||||
|
|
||||||
/* Normalize message. */
|
/* Normalize message. */
|
||||||
Py_INCREF(message); /* DECREF'ed in cleanup. */
|
Py_INCREF(message); /* DECREF'ed in cleanup. */
|
||||||
if (PyObject_TypeCheck(message, (PyTypeObject *)PyExc_Warning)) {
|
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. */
|
/* 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)
|
if (action == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
|
@ -921,7 +891,6 @@ warn_explicit(PyThreadState *tstate, PyObject *category, PyObject *message,
|
||||||
Py_XDECREF(key);
|
Py_XDECREF(key);
|
||||||
Py_XDECREF(text);
|
Py_XDECREF(text);
|
||||||
Py_XDECREF(lineno_obj);
|
Py_XDECREF(lineno_obj);
|
||||||
Py_DECREF(module);
|
|
||||||
Py_XDECREF(message);
|
Py_XDECREF(message);
|
||||||
return result; /* Py_None or NULL. */
|
return result; /* Py_None or NULL. */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue