Skip to content
3 changes: 2 additions & 1 deletion Lib/subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -268,6 +268,7 @@ def _args_from_interpreter_flags():
# -X options
xoptions = getattr(sys, '_xoptions', {})
if 'dev' in xoptions:
args.remove('-Wdefault')
args.extend(('-X', 'dev'))
for opt in ('faulthandler', 'tracemalloc', 'importtime',
'showalloccount', 'showrefcount'):
Expand Down
32 changes: 24 additions & 8 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,31 +541,47 @@ def test_xdev(self):
code = ("import sys, warnings; "
"print(' '.join('%s::%s' % (f[0], f[2].__name__) "
"for f in warnings.filters))")
if sys.flags.debug:
resource_action = "default"
else:
resource_action = "ignore"

out = self.run_xdev("-c", code)
self.assertEqual(out,
"default::Warning "

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

default::Warning must come after BytesWarning. See also the discussion on https://bugs.python.org/issue32230

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment pointing out that Py_BytesWarningFlag means this filter is pretty redundant when neither -b nor -bb is passed.

"ignore::DeprecationWarning "
"ignore::PendingDeprecationWarning "
"ignore::ImportWarning "
"ignore::BytesWarning "
"default::ResourceWarning "
"default::Warning")
f"{resource_action}::ResourceWarning")

out = self.run_xdev("-b", "-c", code)
self.assertEqual(out,
"default::Warning "
"ignore::DeprecationWarning "
"ignore::PendingDeprecationWarning "
"ignore::ImportWarning "
"default::BytesWarning "
"default::ResourceWarning "
"default::Warning")
f"{resource_action}::ResourceWarning")

out = self.run_xdev("-bb", "-c", code)
self.assertEqual(out,
"default::Warning "
"ignore::DeprecationWarning "
"ignore::PendingDeprecationWarning "
"ignore::ImportWarning "
"error::BytesWarning "
"default::ResourceWarning "
"default::Warning")
f"{resource_action}::ResourceWarning")

out = self.run_xdev("-Werror", "-c", code)
self.assertEqual(out,
"error::Warning "
"default::Warning "
"ignore::DeprecationWarning "
"ignore::PendingDeprecationWarning "
"ignore::ImportWarning "
"ignore::BytesWarning "
"default::ResourceWarning "
"default::Warning")
f"{resource_action}::ResourceWarning")

# Memory allocator debug hooks
try:
Expand Down
20 changes: 6 additions & 14 deletions Lib/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,14 +519,11 @@ def _filters_mutated():
# 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)
# TODO: Define a test case that ensures this fallback code always remains
# consistent with init_filters() in _warnings.c
simplefilter("ignore", category=DeprecationWarning, append=1)
simplefilter("ignore", category=PendingDeprecationWarning, append=1)
simplefilter("ignore", category=ImportWarning, append=1)

bytes_warning = sys.flags.bytes_warning
if bytes_warning > 1:
Expand All @@ -538,15 +535,10 @@ def _filters_mutated():
simplefilter(bytes_action, category=BytesWarning, append=1)

# resource usage warnings are enabled by default in pydebug mode
if dev_mode or py_debug:
if sys.flags.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

del _warnings_defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
`-X dev` now injects a ``'default'`` entry into sys.warnoptions, ensuring
that it behaves identically to actually passing ``-Wdefault`` at the command
line.
22 changes: 22 additions & 0 deletions Modules/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -740,11 +740,33 @@ pymain_add_warnings_optlist(_Py_OptList *warnings)
return 0;
}

static int
pymain_add_warnings_dev_mode(_PyCoreConfig *core_config)
{
if (core_config->dev_mode) {
PyObject *option = PyUnicode_FromString("default");
if (option == NULL) {
return -1;
}
if (_PySys_AddWarnOptionWithError(option)) {
Py_DECREF(option);
return -1;
}
Py_DECREF(option);
}
return 0;
}

static int
pymain_add_warnings_options(_PyMain *pymain)
{
PySys_ResetWarnOptions();

if (pymain_add_warnings_dev_mode(&pymain->core_config) < 0) {
pymain->err = _Py_INIT_NO_MEMORY();
return -1;
}

if (pymain_add_warnings_optlist(&pymain->env_warning_options) < 0) {
pymain->err = _Py_INIT_NO_MEMORY();
return -1;
Expand Down
44 changes: 13 additions & 31 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
Expand Up @@ -1172,32 +1172,18 @@ create_filter(PyObject *category, _Py_Identifier *id)
static PyObject *
init_filters(const _PyCoreConfig *config)
{
int dev_mode = config->dev_mode;

Py_ssize_t count = 2;
if (dev_mode) {
count++;
}
#ifndef Py_DEBUG
if (!dev_mode) {
count += 3;
}
#endif
Py_ssize_t count = 5;
PyObject *filters = PyList_New(count);
if (filters == NULL)
return NULL;

size_t pos = 0; /* Post-incremented in each use. */
#ifndef Py_DEBUG
if (!dev_mode) {
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_DeprecationWarning, &PyId_ignore));
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_PendingDeprecationWarning, &PyId_ignore));
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_ImportWarning, &PyId_ignore));
}
#endif
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_DeprecationWarning, &PyId_ignore));
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_PendingDeprecationWarning, &PyId_ignore));
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_ImportWarning, &PyId_ignore));

_Py_Identifier *bytes_action;
if (Py_BytesWarningFlag > 1)
Expand All @@ -1206,23 +1192,19 @@ init_filters(const _PyCoreConfig *config)
bytes_action = &PyId_default;
else
bytes_action = &PyId_ignore;
PyList_SET_ITEM(filters, pos++, create_filter(PyExc_BytesWarning,
bytes_action));
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_BytesWarning, bytes_action));

_Py_Identifier *resource_action;

/* resource usage warnings are enabled by default in pydebug mode */
#ifdef Py_DEBUG
resource_action = &PyId_default;
#else
resource_action = (dev_mode ? &PyId_default: &PyId_ignore);
resource_action = &PyId_ignore;
#endif
PyList_SET_ITEM(filters, pos++, create_filter(PyExc_ResourceWarning,
resource_action));

if (dev_mode) {
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_Warning, &PyId_default));
}
PyList_SET_ITEM(filters, pos++,
create_filter(PyExc_ResourceWarning, resource_action));

for (size_t x = 0; x < pos; x++) {
if (PyList_GET_ITEM(filters, x) == NULL) {
Expand Down