Skip to content

Commit 5e3806f

Browse files
authored
bpo-32101: Add PYTHONDEVMODE environment variable (python#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.
1 parent 706e10b commit 5e3806f

10 files changed

Lines changed: 78 additions & 16 deletions

File tree

Doc/library/development.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ The list of modules described in this chapter is:
2424
unittest.mock-examples.rst
2525
2to3.rst
2626
test.rst
27+
28+
See also the Python development mode: the :option:`-X` ``dev`` option and
29+
:envvar:`PYTHONDEVMODE` environment variable.

Doc/library/sys.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ always available.
334334
:const:`bytes_warning` :option:`-b`
335335
:const:`quiet` :option:`-q`
336336
:const:`hash_randomization` :option:`-R`
337+
:const:`dev_mode` :option:`-X` ``dev``
337338
============================= =============================
338339

339340
.. versionchanged:: 3.2
@@ -345,6 +346,9 @@ always available.
345346
.. versionchanged:: 3.3
346347
Removed obsolete ``division_warning`` attribute.
347348

349+
.. versionchanged:: 3.7
350+
Added ``dev_mode`` attribute for the new :option:`-X` ``dev`` flag.
351+
348352

349353
.. data:: float_info
350354

Doc/using/cmdline.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ Miscellaneous options
411411
nested imports). Note that its output may be broken in multi-threaded
412412
application. Typical usage is ``python3 -X importtime -c 'import
413413
asyncio'``. See also :envvar:`PYTHONPROFILEIMPORTTIME`.
414-
* ``-X dev``: enable CPython's "developer mode", introducing additional
414+
* ``-X dev``: enable CPython's "development mode", introducing additional
415415
runtime checks which are too expensive to be enabled by default. It should
416416
not be more verbose than the default if the code is correct: new warnings
417417
are only emitted when an issue is detected. Effect of the developer mode:
@@ -426,6 +426,8 @@ Miscellaneous options
426426
* Enable the :mod:`faulthandler` module to dump the Python traceback
427427
on a crash.
428428
* Enable :ref:`asyncio debug mode <asyncio-debug-mode>`.
429+
* Set the :attr:`~sys.flags.dev_mode` attribute of :attr:`sys.flags` to
430+
``True``
429431

430432
It also allows passing arbitrary values and retrieving them through the
431433
:data:`sys._xoptions` dictionary.
@@ -796,6 +798,14 @@ conflict.
796798
.. versionadded:: 3.7
797799
See :pep:`538` for more details.
798800

801+
802+
.. envvar:: PYTHONDEVMODE
803+
804+
If this environment variable is set to a non-empty string, enable the
805+
CPython "development mode". See the :option:`-X` ``dev`` option.
806+
807+
.. versionadded:: 3.7
808+
799809
Debug-mode variables
800810
~~~~~~~~~~~~~~~~~~~~
801811

Doc/whatsnew/3.7.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,10 +185,10 @@ resolution on Linux and Windows.
185185
PEP written and implemented by Victor Stinner
186186

187187

188-
New Developer Mode: -X dev
189-
--------------------------
188+
New Development Mode: -X dev
189+
----------------------------
190190

191-
Add a new "developer mode": ``-X dev`` command line option to enable debug
191+
Add a new "development mode": ``-X dev`` command line option to enable debug
192192
checks at runtime.
193193

194194
In short, ``python3 -X dev ...`` behaves as ``PYTHONMALLOC=debug python3 -W
@@ -371,6 +371,11 @@ string
371371
expression pattern for braced placeholders and non-braced placeholders
372372
separately. (Contributed by Barry Warsaw in :issue:`1198569`.)
373373

374+
sys
375+
---
376+
377+
Added :attr:`sys.flags.dev_mode` flag for the new development mode.
378+
374379
time
375380
----
376381

Lib/asyncio/coroutines.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,9 @@ def _is_debug_mode():
2727
# before you define your coroutines. A downside of using this feature
2828
# is that tracebacks show entries for the CoroWrapper.__next__ method
2929
# when _DEBUG is true.
30-
debug = (not sys.flags.ignore_environment and
31-
bool(os.environ.get('PYTHONASYNCIODEBUG')))
32-
if hasattr(sys, '_xoptions') and 'dev' in sys._xoptions:
33-
debug = True
34-
return debug
30+
return (sys.flags.dev_mode
31+
or (not sys.flags.ignore_environment
32+
and bool(os.environ.get('PYTHONASYNCIODEBUG'))))
3533

3634

3735
_DEBUG = _is_debug_mode()

Lib/test/test_cmd_line.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -508,14 +508,18 @@ def test_sys_flags_set(self):
508508
with self.subTest(envar_value=value):
509509
assert_python_ok('-c', code, **env_vars)
510510

511-
def run_xdev(self, *args, check_exitcode=True):
511+
def run_xdev(self, *args, check_exitcode=True, xdev=True):
512512
env = dict(os.environ)
513513
env.pop('PYTHONWARNINGS', None)
514+
env.pop('PYTHONDEVMODE', None)
514515
# Force malloc() to disable the debug hooks which are enabled
515516
# by default for Python compiled in debug mode
516517
env['PYTHONMALLOC'] = 'malloc'
517518

518-
args = (sys.executable, '-X', 'dev', *args)
519+
if xdev:
520+
args = (sys.executable, '-X', 'dev', *args)
521+
else:
522+
args = (sys.executable, *args)
519523
proc = subprocess.run(args,
520524
stdout=subprocess.PIPE,
521525
stderr=subprocess.STDOUT,
@@ -526,6 +530,14 @@ def run_xdev(self, *args, check_exitcode=True):
526530
return proc.stdout.rstrip()
527531

528532
def test_xdev(self):
533+
# sys.flags.dev_mode
534+
code = "import sys; print(sys.flags.dev_mode)"
535+
out = self.run_xdev("-c", code, xdev=False)
536+
self.assertEqual(out, "False")
537+
out = self.run_xdev("-c", code)
538+
self.assertEqual(out, "True")
539+
540+
# Warnings
529541
code = ("import sys, warnings; "
530542
"print(' '.join('%s::%s' % (f[0], f[2].__name__) "
531543
"for f in warnings.filters))")
@@ -555,6 +567,7 @@ def test_xdev(self):
555567
"default::ResourceWarning "
556568
"default::Warning")
557569

570+
# Memory allocator debug hooks
558571
try:
559572
import _testcapi
560573
except ImportError:
@@ -569,6 +582,7 @@ def test_xdev(self):
569582
alloc_name = "malloc_debug"
570583
self.assertEqual(out, alloc_name)
571584

585+
# Faulthandler
572586
try:
573587
import faulthandler
574588
except ImportError:
@@ -581,6 +595,7 @@ def test_xdev(self):
581595
def check_pythonmalloc(self, env_var, name):
582596
code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
583597
env = dict(os.environ)
598+
env.pop('PYTHONDEVMODE', None)
584599
if env_var is not None:
585600
env['PYTHONMALLOC'] = env_var
586601
else:
@@ -621,6 +636,24 @@ def test_pythonmalloc(self):
621636
with self.subTest(env_var=env_var, name=name):
622637
self.check_pythonmalloc(env_var, name)
623638

639+
def test_pythondevmode_env(self):
640+
# Test the PYTHONDEVMODE environment variable
641+
code = "import sys; print(sys.flags.dev_mode)"
642+
env = dict(os.environ)
643+
env.pop('PYTHONDEVMODE', None)
644+
args = (sys.executable, '-c', code)
645+
646+
proc = subprocess.run(args, stdout=subprocess.PIPE,
647+
universal_newlines=True, env=env)
648+
self.assertEqual(proc.stdout.rstrip(), 'False')
649+
self.assertEqual(proc.returncode, 0, proc)
650+
651+
env['PYTHONDEVMODE'] = '1'
652+
proc = subprocess.run(args, stdout=subprocess.PIPE,
653+
universal_newlines=True, env=env)
654+
self.assertEqual(proc.stdout.rstrip(), 'True')
655+
self.assertEqual(proc.returncode, 0, proc)
656+
624657

625658
class IgnoreEnvironmentTest(unittest.TestCase):
626659

Lib/test/test_sys.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -526,10 +526,12 @@ def test_sys_flags(self):
526526
attrs = ("debug",
527527
"inspect", "interactive", "optimize", "dont_write_bytecode",
528528
"no_user_site", "no_site", "ignore_environment", "verbose",
529-
"bytes_warning", "quiet", "hash_randomization", "isolated")
529+
"bytes_warning", "quiet", "hash_randomization", "isolated",
530+
"dev_mode")
530531
for attr in attrs:
531532
self.assertTrue(hasattr(sys.flags, attr), attr)
532-
self.assertEqual(type(getattr(sys.flags, attr)), int, attr)
533+
attr_type = bool if attr == "dev_mode" else int
534+
self.assertEqual(type(getattr(sys.flags, attr)), attr_type, attr)
533535
self.assertTrue(repr(sys.flags))
534536
self.assertEqual(len(sys.flags), len(attrs))
535537

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add :attr:`sys.flags.dev_mode` flag

Modules/main.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,8 @@ static const char usage_6[] =
124124
" hooks.\n"
125125
"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\n"
126126
" coercion behavior. Use PYTHONCOERCECLOCALE=warn to request display of\n"
127-
" locale coercion and locale compatibility warnings on stderr.\n";
127+
" locale coercion and locale compatibility warnings on stderr.\n"
128+
"PYTHONDEVMODE: enable the development mode.\n";
128129

129130
static void
130131
pymain_usage(int error, const wchar_t* program)
@@ -1520,7 +1521,9 @@ pymain_parse_envvars(_PyMain *pymain)
15201521
if (pymain_init_tracemalloc(pymain) < 0) {
15211522
return -1;
15221523
}
1523-
if (pymain_get_xoption(pymain, L"dev")) {
1524+
if (pymain_get_xoption(pymain, L"dev" ) ||
1525+
pymain_get_env_var("PYTHONDEVMODE"))
1526+
{
15241527
core_config->dev_mode = 1;
15251528
core_config->faulthandler = 1;
15261529
core_config->allocator = "debug";

Python/sysmodule.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1814,21 +1814,23 @@ static PyStructSequence_Field flags_fields[] = {
18141814
{"quiet", "-q"},
18151815
{"hash_randomization", "-R"},
18161816
{"isolated", "-I"},
1817+
{"dev_mode", "-X dev"},
18171818
{0}
18181819
};
18191820

18201821
static PyStructSequence_Desc flags_desc = {
18211822
"sys.flags", /* name */
18221823
flags__doc__, /* doc */
18231824
flags_fields, /* fields */
1824-
13
1825+
14
18251826
};
18261827

18271828
static PyObject*
18281829
make_flags(void)
18291830
{
18301831
int pos = 0;
18311832
PyObject *seq;
1833+
_PyCoreConfig *core_config = &_PyGILState_GetInterpreterStateUnsafe()->core_config;
18321834

18331835
seq = PyStructSequence_New(&FlagsType);
18341836
if (seq == NULL)
@@ -1853,6 +1855,7 @@ make_flags(void)
18531855
SetFlag(Py_HashRandomizationFlag);
18541856
SetFlag(Py_IsolatedFlag);
18551857
#undef SetFlag
1858+
PyStructSequence_SET_ITEM(seq, pos++, PyBool_FromLong(core_config->dev_mode));
18561859

18571860
if (PyErr_Occurred()) {
18581861
Py_DECREF(seq);

0 commit comments

Comments
 (0)