summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-12-12 21:59:48 (GMT)
committerGitHub <noreply@github.com>2017-12-12 21:59:48 (GMT)
commit747f48e2e92390c44c72f52a1239959601cde157 (patch)
tree502e53b129aee7a393ca6d05e4f93751919a5e1b /Lib
parentb748e3b2586e44bfc7011b601bce9cc6d16d89f1 (diff)
downloadcpython-747f48e2e92390c44c72f52a1239959601cde157.zip
cpython-747f48e2e92390c44c72f52a1239959601cde157.tar.gz
cpython-747f48e2e92390c44c72f52a1239959601cde157.tar.bz2
bpo-32230: Set sys.warnoptions with -X dev (#4820)
Rather than supporting dev mode directly in the warnings module, this instead adjusts the initialisation code to add an extra 'default' entry to sys.warnoptions when dev mode is enabled. This ensures that dev mode behaves *exactly* as if `-Wdefault` had been passed on the command line, including in the way it interacts with `sys.warnoptions`, and with other command line flags like `-bb`. Fix also bpo-20361: have -b & -bb options take precedence over any other warnings options. Patch written by Nick Coghlan, with minor modifications of Victor Stinner.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/subprocess.py18
-rw-r--r--Lib/test/test_cmd_line.py81
-rw-r--r--Lib/test/test_warnings/__init__.py15
-rw-r--r--Lib/unittest/test/test_runner.py2
-rw-r--r--Lib/warnings.py35
5 files changed, 91 insertions, 60 deletions
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
index 35bfddd..301433c 100644
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -241,7 +241,7 @@ def _optim_args_from_interpreter_flags():
def _args_from_interpreter_flags():
"""Return a list of command-line arguments reproducing the current
- settings in sys.flags and sys.warnoptions."""
+ settings in sys.flags, sys.warnoptions and sys._xoptions."""
flag_opt_map = {
'debug': 'd',
# 'inspect': 'i',
@@ -262,12 +262,22 @@ def _args_from_interpreter_flags():
args.append('-' + opt * v)
# -W options
- for opt in sys.warnoptions:
+ warnopts = sys.warnoptions[:]
+ bytes_warning = sys.flags.bytes_warning
+ xoptions = getattr(sys, '_xoptions', {})
+ dev_mode = ('dev' in xoptions)
+
+ if bytes_warning > 1:
+ warnopts.remove("error::BytesWarning")
+ elif bytes_warning:
+ warnopts.remove("default::BytesWarning")
+ if dev_mode:
+ warnopts.remove('default')
+ for opt in warnopts:
args.append('-W' + opt)
# -X options
- xoptions = getattr(sys, '_xoptions', {})
- if 'dev' in xoptions:
+ if dev_mode:
args.extend(('-X', 'dev'))
for opt in ('faulthandler', 'tracemalloc', 'importtime',
'showalloccount', 'showrefcount'):
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 383302b..2aff51b 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -14,6 +14,11 @@ from test.support.script_helper import (
interpreter_requires_environment
)
+
+# Debug build?
+Py_DEBUG = hasattr(sys, "gettotalrefcount")
+
+
# XXX (ncoghlan): Move to script_helper and make consistent with run_python
def _kill_python_and_exit_code(p):
data = kill_python(p)
@@ -97,7 +102,7 @@ class CmdLineTest(unittest.TestCase):
# "-X showrefcount" shows the refcount, but only in debug builds
rc, out, err = run_python('-X', 'showrefcount', '-c', code)
self.assertEqual(out.rstrip(), b"{'showrefcount': True}")
- if hasattr(sys, 'gettotalrefcount'): # debug build
+ if Py_DEBUG:
self.assertRegex(err, br'^\[\d+ refs, \d+ blocks\]')
else:
self.assertEqual(err, b'')
@@ -541,31 +546,26 @@ class CmdLineTest(unittest.TestCase):
code = ("import sys, warnings; "
"print(' '.join('%s::%s' % (f[0], f[2].__name__) "
"for f in warnings.filters))")
+ if Py_DEBUG:
+ expected_filters = "default::Warning"
+ else:
+ expected_filters = ("default::Warning "
+ "ignore::DeprecationWarning "
+ "ignore::PendingDeprecationWarning "
+ "ignore::ImportWarning "
+ "ignore::ResourceWarning")
out = self.run_xdev("-c", code)
- self.assertEqual(out,
- "ignore::BytesWarning "
- "default::ResourceWarning "
- "default::Warning")
+ self.assertEqual(out, expected_filters)
out = self.run_xdev("-b", "-c", code)
- self.assertEqual(out,
- "default::BytesWarning "
- "default::ResourceWarning "
- "default::Warning")
+ self.assertEqual(out, f"default::BytesWarning {expected_filters}")
out = self.run_xdev("-bb", "-c", code)
- self.assertEqual(out,
- "error::BytesWarning "
- "default::ResourceWarning "
- "default::Warning")
+ self.assertEqual(out, f"error::BytesWarning {expected_filters}")
out = self.run_xdev("-Werror", "-c", code)
- self.assertEqual(out,
- "error::Warning "
- "ignore::BytesWarning "
- "default::ResourceWarning "
- "default::Warning")
+ self.assertEqual(out, f"error::Warning {expected_filters}")
# Memory allocator debug hooks
try:
@@ -592,6 +592,46 @@ class CmdLineTest(unittest.TestCase):
out = self.run_xdev("-c", code)
self.assertEqual(out, "True")
+ def check_warnings_filters(self, cmdline_option, envvar, use_pywarning=False):
+ if use_pywarning:
+ code = ("import sys; from test.support import import_fresh_module; "
+ "warnings = import_fresh_module('warnings', blocked=['_warnings']); ")
+ else:
+ code = "import sys, warnings; "
+ code += ("print(' '.join('%s::%s' % (f[0], f[2].__name__) "
+ "for f in warnings.filters))")
+ args = (sys.executable, '-W', cmdline_option, '-bb', '-c', code)
+ env = dict(os.environ)
+ env.pop('PYTHONDEVMODE', None)
+ env["PYTHONWARNINGS"] = envvar
+ proc = subprocess.run(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ universal_newlines=True,
+ env=env)
+ self.assertEqual(proc.returncode, 0, proc)
+ return proc.stdout.rstrip()
+
+ def test_warnings_filter_precedence(self):
+ expected_filters = ("error::BytesWarning "
+ "once::UserWarning "
+ "always::UserWarning")
+ if not Py_DEBUG:
+ expected_filters += (" "
+ "ignore::DeprecationWarning "
+ "ignore::PendingDeprecationWarning "
+ "ignore::ImportWarning "
+ "ignore::ResourceWarning")
+
+ out = self.check_warnings_filters("once::UserWarning",
+ "always::UserWarning")
+ self.assertEqual(out, expected_filters)
+
+ out = self.check_warnings_filters("once::UserWarning",
+ "always::UserWarning",
+ use_pywarning=True)
+ self.assertEqual(out, expected_filters)
+
def check_pythonmalloc(self, env_var, name):
code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
env = dict(os.environ)
@@ -611,13 +651,12 @@ class CmdLineTest(unittest.TestCase):
def test_pythonmalloc(self):
# Test the PYTHONMALLOC environment variable
- pydebug = hasattr(sys, "gettotalrefcount")
pymalloc = support.with_pymalloc()
if pymalloc:
- default_name = 'pymalloc_debug' if pydebug else 'pymalloc'
+ default_name = 'pymalloc_debug' if Py_DEBUG else 'pymalloc'
default_name_debug = 'pymalloc_debug'
else:
- default_name = 'malloc_debug' if pydebug else 'malloc'
+ default_name = 'malloc_debug' if Py_DEBUG else 'malloc'
default_name_debug = 'malloc_debug'
tests = [
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index e60bc4d..039c96e 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -1110,20 +1110,23 @@ class EnvironmentVariableTests(BaseTest):
def test_single_warning(self):
rc, stdout, stderr = assert_python_ok("-c",
"import sys; sys.stdout.write(str(sys.warnoptions))",
- PYTHONWARNINGS="ignore::DeprecationWarning")
+ PYTHONWARNINGS="ignore::DeprecationWarning",
+ PYTHONDEVMODE="")
self.assertEqual(stdout, b"['ignore::DeprecationWarning']")
def test_comma_separated_warnings(self):
rc, stdout, stderr = assert_python_ok("-c",
"import sys; sys.stdout.write(str(sys.warnoptions))",
- PYTHONWARNINGS="ignore::DeprecationWarning,ignore::UnicodeWarning")
+ PYTHONWARNINGS="ignore::DeprecationWarning,ignore::UnicodeWarning",
+ PYTHONDEVMODE="")
self.assertEqual(stdout,
b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']")
def test_envvar_and_command_line(self):
rc, stdout, stderr = assert_python_ok("-Wignore::UnicodeWarning", "-c",
"import sys; sys.stdout.write(str(sys.warnoptions))",
- PYTHONWARNINGS="ignore::DeprecationWarning")
+ PYTHONWARNINGS="ignore::DeprecationWarning",
+ PYTHONDEVMODE="")
self.assertEqual(stdout,
b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']")
@@ -1131,7 +1134,8 @@ class EnvironmentVariableTests(BaseTest):
rc, stdout, stderr = assert_python_failure("-Werror::DeprecationWarning", "-c",
"import sys, warnings; sys.stdout.write(str(sys.warnoptions)); "
"warnings.warn('Message', DeprecationWarning)",
- PYTHONWARNINGS="default::DeprecationWarning")
+ PYTHONWARNINGS="default::DeprecationWarning",
+ PYTHONDEVMODE="")
self.assertEqual(stdout,
b"['default::DeprecationWarning', 'error::DeprecationWarning']")
self.assertEqual(stderr.splitlines(),
@@ -1145,7 +1149,8 @@ class EnvironmentVariableTests(BaseTest):
rc, stdout, stderr = assert_python_ok("-c",
"import sys; sys.stdout.write(str(sys.warnoptions))",
PYTHONIOENCODING="utf-8",
- PYTHONWARNINGS="ignore:DeprecaciónWarning")
+ PYTHONWARNINGS="ignore:DeprecaciónWarning",
+ PYTHONDEVMODE="")
self.assertEqual(stdout,
"['ignore:DeprecaciónWarning']".encode('utf-8'))
diff --git a/Lib/unittest/test/test_runner.py b/Lib/unittest/test/test_runner.py
index ddc498c..3c40056 100644
--- a/Lib/unittest/test/test_runner.py
+++ b/Lib/unittest/test/test_runner.py
@@ -289,7 +289,7 @@ class Test_TextTestRunner(unittest.TestCase):
at_msg = b'Please use assertTrue instead.'
# no args -> all the warnings are printed, unittest warnings only once
- p = subprocess.Popen([sys.executable, '_test_warnings.py'], **opts)
+ p = subprocess.Popen([sys.executable, '-E', '_test_warnings.py'], **opts)
with p:
out, err = get_parse_out_err(p)
self.assertIn(b'OK', err)
diff --git a/Lib/warnings.py b/Lib/warnings.py
index 4e7241f..f4331c8 100644
--- a/Lib/warnings.py
+++ b/Lib/warnings.py
@@ -519,34 +519,11 @@ except ImportError:
# Module initialization
_processoptions(sys.warnoptions)
if not _warnings_defaults:
- dev_mode = ('dev' in getattr(sys, '_xoptions', {}))
- py_debug = hasattr(sys, 'gettotalrefcount')
-
- if not(dev_mode or py_debug):
- silence = [ImportWarning, PendingDeprecationWarning]
- silence.append(DeprecationWarning)
- for cls in silence:
- simplefilter("ignore", category=cls)
-
- bytes_warning = sys.flags.bytes_warning
- if bytes_warning > 1:
- bytes_action = "error"
- elif bytes_warning:
- bytes_action = "default"
- else:
- bytes_action = "ignore"
- simplefilter(bytes_action, category=BytesWarning, append=1)
-
- # resource usage warnings are enabled by default in pydebug mode
- if dev_mode or py_debug:
- resource_action = "default"
- else:
- resource_action = "ignore"
- simplefilter(resource_action, category=ResourceWarning, append=1)
-
- if dev_mode:
- simplefilter("default", category=Warning, append=1)
-
- del py_debug, dev_mode
+ # Several warning categories are ignored by default in Py_DEBUG builds
+ if not hasattr(sys, 'gettotalrefcount'):
+ simplefilter("ignore", category=DeprecationWarning, append=1)
+ simplefilter("ignore", category=PendingDeprecationWarning, append=1)
+ simplefilter("ignore", category=ImportWarning, append=1)
+ simplefilter("ignore", category=ResourceWarning, append=1)
del _warnings_defaults