Skip to content

Commit

Permalink
bpo-32101: Add PYTHONDEVMODE environment variable (#4624)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
vstinner committed Nov 30, 2017
1 parent 706e10b commit 5e3806f
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 16 deletions.
3 changes: 3 additions & 0 deletions Doc/library/development.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ The list of modules described in this chapter is:
unittest.mock-examples.rst
2to3.rst
test.rst

See also the Python development mode: the :option:`-X` ``dev`` option and
:envvar:`PYTHONDEVMODE` environment variable.
4 changes: 4 additions & 0 deletions Doc/library/sys.rst
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ always available.
:const:`bytes_warning` :option:`-b`
:const:`quiet` :option:`-q`
:const:`hash_randomization` :option:`-R`
:const:`dev_mode` :option:`-X` ``dev``
============================= =============================

.. versionchanged:: 3.2
Expand All @@ -345,6 +346,9 @@ always available.
.. versionchanged:: 3.3
Removed obsolete ``division_warning`` attribute.

.. versionchanged:: 3.7
Added ``dev_mode`` attribute for the new :option:`-X` ``dev`` flag.


.. data:: float_info

Expand Down
12 changes: 11 additions & 1 deletion Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ Miscellaneous options
nested imports). Note that its output may be broken in multi-threaded
application. Typical usage is ``python3 -X importtime -c 'import
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
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:
Expand All @@ -426,6 +426,8 @@ Miscellaneous options
* Enable the :mod:`faulthandler` module to dump the Python traceback
on a crash.
* 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
:data:`sys._xoptions` dictionary.
Expand Down Expand Up @@ -796,6 +798,14 @@ conflict.
.. versionadded:: 3.7
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
~~~~~~~~~~~~~~~~~~~~

Expand Down
11 changes: 8 additions & 3 deletions Doc/whatsnew/3.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,10 @@ resolution on Linux and Windows.
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.

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

sys
---

Added :attr:`sys.flags.dev_mode` flag for the new development mode.

time
----

Expand Down
8 changes: 3 additions & 5 deletions Lib/asyncio/coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ def _is_debug_mode():
# before you define your coroutines. A downside of using this feature
# is that tracebacks show entries for the CoroWrapper.__next__ method
# when _DEBUG is true.
debug = (not sys.flags.ignore_environment and
bool(os.environ.get('PYTHONASYNCIODEBUG')))
if hasattr(sys, '_xoptions') and 'dev' in sys._xoptions:
debug = True
return debug
return (sys.flags.dev_mode
or (not sys.flags.ignore_environment
and bool(os.environ.get('PYTHONASYNCIODEBUG'))))


_DEBUG = _is_debug_mode()
Expand Down
37 changes: 35 additions & 2 deletions Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,18 @@ def test_sys_flags_set(self):
with self.subTest(envar_value=value):
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.pop('PYTHONWARNINGS', None)
env.pop('PYTHONDEVMODE', None)
# Force malloc() to disable the debug hooks which are enabled
# by default for Python compiled in debug mode
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,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
Expand All @@ -526,6 +530,14 @@ def run_xdev(self, *args, check_exitcode=True):
return proc.stdout.rstrip()

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; "
"print(' '.join('%s::%s' % (f[0], f[2].__name__) "
"for f in warnings.filters))")
Expand Down Expand Up @@ -555,6 +567,7 @@ def test_xdev(self):
"default::ResourceWarning "
"default::Warning")

# Memory allocator debug hooks
try:
import _testcapi
except ImportError:
Expand All @@ -569,6 +582,7 @@ def test_xdev(self):
alloc_name = "malloc_debug"
self.assertEqual(out, alloc_name)

# Faulthandler
try:
import faulthandler
except ImportError:
Expand All @@ -581,6 +595,7 @@ def test_xdev(self):
def check_pythonmalloc(self, env_var, name):
code = 'import _testcapi; print(_testcapi.pymem_getallocatorsname())'
env = dict(os.environ)
env.pop('PYTHONDEVMODE', None)
if env_var is not None:
env['PYTHONMALLOC'] = env_var
else:
Expand Down Expand Up @@ -621,6 +636,24 @@ def test_pythonmalloc(self):
with self.subTest(env_var=env_var, name=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):

Expand Down
6 changes: 4 additions & 2 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,12 @@ def test_sys_flags(self):
attrs = ("debug",
"inspect", "interactive", "optimize", "dont_write_bytecode",
"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:
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.assertEqual(len(sys.flags), len(attrs))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`sys.flags.dev_mode` flag
7 changes: 5 additions & 2 deletions Modules/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ static const char usage_6[] =
" hooks.\n"
"PYTHONCOERCECLOCALE: if this variable is set to 0, it disables the locale\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
pymain_usage(int error, const wchar_t* program)
Expand Down Expand Up @@ -1520,7 +1521,9 @@ pymain_parse_envvars(_PyMain *pymain)
if (pymain_init_tracemalloc(pymain) < 0) {
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->faulthandler = 1;
core_config->allocator = "debug";
Expand Down
5 changes: 4 additions & 1 deletion Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1814,21 +1814,23 @@ static PyStructSequence_Field flags_fields[] = {
{"quiet", "-q"},
{"hash_randomization", "-R"},
{"isolated", "-I"},
{"dev_mode", "-X dev"},
{0}
};

static PyStructSequence_Desc flags_desc = {
"sys.flags", /* name */
flags__doc__, /* doc */
flags_fields, /* fields */
13
14
};

static PyObject*
make_flags(void)
{
int pos = 0;
PyObject *seq;
_PyCoreConfig *core_config = &_PyGILState_GetInterpreterStateUnsafe()->core_config;

seq = PyStructSequence_New(&FlagsType);
if (seq == NULL)
Expand All @@ -1853,6 +1855,7 @@ make_flags(void)
SetFlag(Py_HashRandomizationFlag);
SetFlag(Py_IsolatedFlag);
#undef SetFlag
PyStructSequence_SET_ITEM(seq, pos++, PyBool_FromLong(core_config->dev_mode));

if (PyErr_Occurred()) {
Py_DECREF(seq);
Expand Down

0 comments on commit 5e3806f

Please sign in to comment.