Unverified Kaydet (Commit) 5e3806f8 authored tarafından Victor Stinner's avatar Victor Stinner Kaydeden (comit) GitHub

bpo-32101: Add PYTHONDEVMODE environment variable (#4624)

* bpo-32101: Add sys.flags.dev_mode flag
  Rename also the "Developer mode" to the "Development mode".
* bpo-32101: Add PYTHONDEVMODE environment variable
  Mention it in the development chapiter.
üst 706e10b1
...@@ -24,3 +24,6 @@ The list of modules described in this chapter is: ...@@ -24,3 +24,6 @@ The list of modules described in this chapter is:
unittest.mock-examples.rst unittest.mock-examples.rst
2to3.rst 2to3.rst
test.rst test.rst
See also the Python development mode: the :option:`-X` ``dev`` option and
:envvar:`PYTHONDEVMODE` environment variable.
...@@ -334,6 +334,7 @@ always available. ...@@ -334,6 +334,7 @@ always available.
:const:`bytes_warning` :option:`-b` :const:`bytes_warning` :option:`-b`
:const:`quiet` :option:`-q` :const:`quiet` :option:`-q`
:const:`hash_randomization` :option:`-R` :const:`hash_randomization` :option:`-R`
:const:`dev_mode` :option:`-X` ``dev``
============================= ============================= ============================= =============================
.. versionchanged:: 3.2 .. versionchanged:: 3.2
...@@ -345,6 +346,9 @@ always available. ...@@ -345,6 +346,9 @@ always available.
.. versionchanged:: 3.3 .. versionchanged:: 3.3
Removed obsolete ``division_warning`` attribute. Removed obsolete ``division_warning`` attribute.
.. versionchanged:: 3.7
Added ``dev_mode`` attribute for the new :option:`-X` ``dev`` flag.
.. data:: float_info .. data:: float_info
......
...@@ -411,7 +411,7 @@ Miscellaneous options ...@@ -411,7 +411,7 @@ Miscellaneous options
nested imports). Note that its output may be broken in multi-threaded nested imports). Note that its output may be broken in multi-threaded
application. Typical usage is ``python3 -X importtime -c 'import application. Typical usage is ``python3 -X importtime -c 'import
asyncio'``. See also :envvar:`PYTHONPROFILEIMPORTTIME`. asyncio'``. See also :envvar:`PYTHONPROFILEIMPORTTIME`.
* ``-X dev``: enable CPython's "developer mode", introducing additional * ``-X dev``: enable CPython's "development mode", introducing additional
runtime checks which are too expensive to be enabled by default. It should runtime checks which are too expensive to be enabled by default. It should
not be more verbose than the default if the code is correct: new warnings not be more verbose than the default if the code is correct: new warnings
are only emitted when an issue is detected. Effect of the developer mode: are only emitted when an issue is detected. Effect of the developer mode:
...@@ -426,6 +426,8 @@ Miscellaneous options ...@@ -426,6 +426,8 @@ Miscellaneous options
* Enable the :mod:`faulthandler` module to dump the Python traceback * Enable the :mod:`faulthandler` module to dump the Python traceback
on a crash. on a crash.
* Enable :ref:`asyncio debug mode <asyncio-debug-mode>`. * Enable :ref:`asyncio debug mode <asyncio-debug-mode>`.
* Set the :attr:`~sys.flags.dev_mode` attribute of :attr:`sys.flags` to
``True``
It also allows passing arbitrary values and retrieving them through the It also allows passing arbitrary values and retrieving them through the
:data:`sys._xoptions` dictionary. :data:`sys._xoptions` dictionary.
...@@ -796,6 +798,14 @@ conflict. ...@@ -796,6 +798,14 @@ conflict.
.. versionadded:: 3.7 .. versionadded:: 3.7
See :pep:`538` for more details. See :pep:`538` for more details.
.. envvar:: PYTHONDEVMODE
If this environment variable is set to a non-empty string, enable the
CPython "development mode". See the :option:`-X` ``dev`` option.
.. versionadded:: 3.7
Debug-mode variables Debug-mode variables
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
......
...@@ -185,10 +185,10 @@ resolution on Linux and Windows. ...@@ -185,10 +185,10 @@ resolution on Linux and Windows.
PEP written and implemented by Victor Stinner PEP written and implemented by Victor Stinner
New Developer Mode: -X dev New Development Mode: -X dev
-------------------------- ----------------------------
Add a new "developer mode": ``-X dev`` command line option to enable debug Add a new "development mode": ``-X dev`` command line option to enable debug
checks at runtime. checks at runtime.
In short, ``python3 -X dev ...`` behaves as ``PYTHONMALLOC=debug python3 -W In short, ``python3 -X dev ...`` behaves as ``PYTHONMALLOC=debug python3 -W
...@@ -371,6 +371,11 @@ string ...@@ -371,6 +371,11 @@ string
expression pattern for braced placeholders and non-braced placeholders expression pattern for braced placeholders and non-braced placeholders
separately. (Contributed by Barry Warsaw in :issue:`1198569`.) separately. (Contributed by Barry Warsaw in :issue:`1198569`.)
sys
---
Added :attr:`sys.flags.dev_mode` flag for the new development mode.
time time
---- ----
......
...@@ -27,11 +27,9 @@ def _is_debug_mode(): ...@@ -27,11 +27,9 @@ def _is_debug_mode():
# before you define your coroutines. A downside of using this feature # before you define your coroutines. A downside of using this feature
# is that tracebacks show entries for the CoroWrapper.__next__ method # is that tracebacks show entries for the CoroWrapper.__next__ method
# when _DEBUG is true. # when _DEBUG is true.
debug = (not sys.flags.ignore_environment and return (sys.flags.dev_mode
bool(os.environ.get('PYTHONASYNCIODEBUG'))) or (not sys.flags.ignore_environment
if hasattr(sys, '_xoptions') and 'dev' in sys._xoptions: and bool(os.environ.get('PYTHONASYNCIODEBUG'))))
debug = True
return debug
_DEBUG = _is_debug_mode() _DEBUG = _is_debug_mode()
......
...@@ -508,14 +508,18 @@ class CmdLineTest(unittest.TestCase): ...@@ -508,14 +508,18 @@ class CmdLineTest(unittest.TestCase):
with self.subTest(envar_value=value): with self.subTest(envar_value=value):
assert_python_ok('-c', code, **env_vars) assert_python_ok('-c', code, **env_vars)
def run_xdev(self, *args, check_exitcode=True): def run_xdev(self, *args, check_exitcode=True, xdev=True):
env = dict(os.environ) env = dict(os.environ)
env.pop('PYTHONWARNINGS', None) env.pop('PYTHONWARNINGS', None)
env.pop('PYTHONDEVMODE', None)
# Force malloc() to disable the debug hooks which are enabled # Force malloc() to disable the debug hooks which are enabled
# by default for Python compiled in debug mode # by default for Python compiled in debug mode
env['PYTHONMALLOC'] = 'malloc' env['PYTHONMALLOC'] = 'malloc'
args = (sys.executable, '-X', 'dev', *args) if xdev:
args = (sys.executable, '-X', 'dev', *args)
else:
args = (sys.executable, *args)
proc = subprocess.run(args, proc = subprocess.run(args,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
...@@ -526,6 +530,14 @@ class CmdLineTest(unittest.TestCase): ...@@ -526,6 +530,14 @@ class CmdLineTest(unittest.TestCase):
return proc.stdout.rstrip() return proc.stdout.rstrip()
def test_xdev(self): def test_xdev(self):
# sys.flags.dev_mode
code = "import sys; print(sys.flags.dev_mode)"
out = self.run_xdev("-c", code, xdev=False)
self.assertEqual(out, "False")
out = self.run_xdev("-c", code)
self.assertEqual(out, "True")
# Warnings
code = ("import sys, warnings; " code = ("import sys, warnings; "
"print(' '.join('%s::%s' % (f[0], f[2].__name__) " "print(' '.join('%s::%s' % (f[0], f[2].__name__) "
"for f in warnings.filters))") "for f in warnings.filters))")
...@@ -555,6 +567,7 @@ class CmdLineTest(unittest.TestCase): ...@@ -555,6 +567,7 @@ class CmdLineTest(unittest.TestCase):
"default::ResourceWarning " "default::ResourceWarning "
"default::Warning") "default::Warning")
# Memory allocator debug hooks
try: try:
import _testcapi import _testcapi
except ImportError: except ImportError:
...@@ -569,6 +582,7 @@ class CmdLineTest(unittest.TestCase): ...@@ -569,6 +582,7 @@ class CmdLineTest(unittest.TestCase):
alloc_name = "malloc_debug" alloc_name = "malloc_debug"
self.assertEqual(out, alloc_name) self.assertEqual(out, alloc_name)
# Faulthandler
try: try:
import faulthandler import faulthandler
except ImportError: except ImportError:
...@@ -581,6 +595,7 @@ class CmdLineTest(unittest.TestCase): ...@@ -581,6 +595,7 @@ class CmdLineTest(unittest.TestCase):
def check_pythonmalloc(self, env_var, name): def check_pythonmalloc(self, env_var, name):
code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())' code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
env = dict(os.environ) env = dict(os.environ)
env.pop('PYTHONDEVMODE', None)
if env_var is not None: if env_var is not None:
env['PYTHONMALLOC'] = env_var env['PYTHONMALLOC'] = env_var
else: else:
...@@ -621,6 +636,24 @@ class CmdLineTest(unittest.TestCase): ...@@ -621,6 +636,24 @@ class CmdLineTest(unittest.TestCase):
with self.subTest(env_var=env_var, name=name): with self.subTest(env_var=env_var, name=name):
self.check_pythonmalloc(env_var, name) self.check_pythonmalloc(env_var, name)
def test_pythondevmode_env(self):
# Test the PYTHONDEVMODE environment variable
code = "import sys; print(sys.flags.dev_mode)"
env = dict(os.environ)
env.pop('PYTHONDEVMODE', None)
args = (sys.executable, '-c', code)
proc = subprocess.run(args, stdout=subprocess.PIPE,
universal_newlines=True, env=env)
self.assertEqual(proc.stdout.rstrip(), 'False')
self.assertEqual(proc.returncode, 0, proc)
env['PYTHONDEVMODE'] = '1'
proc = subprocess.run(args, stdout=subprocess.PIPE,
universal_newlines=True, env=env)
self.assertEqual(proc.stdout.rstrip(), 'True')
self.assertEqual(proc.returncode, 0, proc)
class IgnoreEnvironmentTest(unittest.TestCase): class IgnoreEnvironmentTest(unittest.TestCase):
......
...@@ -526,10 +526,12 @@ class SysModuleTest(unittest.TestCase): ...@@ -526,10 +526,12 @@ class SysModuleTest(unittest.TestCase):
attrs = ("debug", attrs = ("debug",
"inspect", "interactive", "optimize", "dont_write_bytecode", "inspect", "interactive", "optimize", "dont_write_bytecode",
"no_user_site", "no_site", "ignore_environment", "verbose", "no_user_site", "no_site", "ignore_environment", "verbose",
"bytes_warning", "quiet", "hash_randomization", "isolated") "bytes_warning", "quiet", "hash_randomization", "isolated",
"dev_mode")
for attr in attrs: for attr in attrs:
self.assertTrue(hasattr(sys.flags, attr), attr) self.assertTrue(hasattr(sys.flags, attr), attr)
self.assertEqual(type(getattr(sys.flags, attr)), int, attr) attr_type = bool if attr == "dev_mode" else int
self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr)
self.assertTrue(repr(sys.flags)) self.assertTrue(repr(sys.flags))
self.assertEqual(len(sys.flags), len(attrs)) self.assertEqual(len(sys.flags), len(attrs))
......
...@@ -124,7 +124,8 @@ static const char usage_6[] = ...@@ -124,7 +124,8 @@ static const char usage_6[] =
" hooks.\n" " hooks.\n"
"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n" "PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n"
" coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n" " coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n"
" locale coercion and locale compatibility warnings on stderr.\n"; " locale coercion and locale compatibility warnings on stderr.\n"
"PYTHONDEVMODE: enable the development mode.\n";
static void static void
pymain_usage(int error, const wchar_t* program) pymain_usage(int error, const wchar_t* program)
...@@ -1520,7 +1521,9 @@ pymain_parse_envvars(_PyMain *pymain) ...@@ -1520,7 +1521,9 @@ pymain_parse_envvars(_PyMain *pymain)
if (pymain_init_tracemalloc(pymain) < 0) { if (pymain_init_tracemalloc(pymain) < 0) {
return -1; return -1;
} }
if (pymain_get_xoption(pymain, L"dev")) { if (pymain_get_xoption(pymain, L"dev" ) ||
pymain_get_env_var("PYTHONDEVMODE"))
{
core_config->dev_mode = 1; core_config->dev_mode = 1;
core_config->faulthandler = 1; core_config->faulthandler = 1;
core_config->allocator = "debug"; core_config->allocator = "debug";
......
...@@ -1814,6 +1814,7 @@ static PyStructSequence_Field flags_fields[] = { ...@@ -1814,6 +1814,7 @@ static PyStructSequence_Field flags_fields[] = {
{"quiet", "-q"}, {"quiet", "-q"},
{"hash_randomization", "-R"}, {"hash_randomization", "-R"},
{"isolated", "-I"}, {"isolated", "-I"},
{"dev_mode", "-X dev"},
{0} {0}
}; };
...@@ -1821,7 +1822,7 @@ static PyStructSequence_Desc flags_desc = { ...@@ -1821,7 +1822,7 @@ static PyStructSequence_Desc flags_desc = {
"sys.flags", /* name */ "sys.flags", /* name */
flags__doc__, /* doc */ flags__doc__, /* doc */
flags_fields, /* fields */ flags_fields, /* fields */
13 14
}; };
static PyObject* static PyObject*
...@@ -1829,6 +1830,7 @@ make_flags(void) ...@@ -1829,6 +1830,7 @@ make_flags(void)
{ {
int pos = 0; int pos = 0;
PyObject *seq; PyObject *seq;
_PyCoreConfig *core_config = &_PyGILState_GetInterpreterStateUnsafe()->core_config;
seq = PyStructSequence_New(&FlagsType); seq = PyStructSequence_New(&FlagsType);
if (seq == NULL) if (seq == NULL)
...@@ -1853,6 +1855,7 @@ make_flags(void) ...@@ -1853,6 +1855,7 @@ make_flags(void)
SetFlag(Py_HashRandomizationFlag); SetFlag(Py_HashRandomizationFlag);
SetFlag(Py_IsolatedFlag); SetFlag(Py_IsolatedFlag);
#undef SetFlag #undef SetFlag
PyStructSequence_SET_ITEM(seq, pos++, PyBool_FromLong(core_config->dev_mode));
if (PyErr_Occurred()) { if (PyErr_Occurred()) {
Py_DECREF(seq); Py_DECREF(seq);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment