]> granicus.if.org Git - python/commitdiff
bpo-32101: Add PYTHONDEVMODE environment variable (#4624)
authorVictor Stinner <victor.stinner@gmail.com>
Thu, 30 Nov 2017 10:40:24 +0000 (11:40 +0100)
committerGitHub <noreply@github.com>
Thu, 30 Nov 2017 10:40:24 +0000 (11:40 +0100)
* 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.

Doc/library/development.rst
Doc/library/sys.rst
Doc/using/cmdline.rst
Doc/whatsnew/3.7.rst
Lib/asyncio/coroutines.py
Lib/test/test_cmd_line.py
Lib/test/test_sys.py
Misc/NEWS.d/next/Library/2017-11-29-00-42-47.bpo-321010.-axD5l.rst [new file with mode: 0644]
Modules/main.c
Python/sysmodule.c

index d2b5fa2aa4f5f827f17da4fb89364f07c83aea87..ab34e1f7ce5f64e767ae75cefe13ce7ed28ffb55 100644 (file)
@@ -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.
index faf540c4ea43b21e5c513f28394e8baea2a07d31..9e47681804b5eb58cc5d447d432cd23b860949a9 100644 (file)
@@ -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
@@ -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
 
index e6189fd8127fe55ccca8644e000cf62b710e5f87..d110ae3baf28004c45c2019a471bab9413b2fd64 100644 (file)
@@ -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:
@@ -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.
@@ -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
 ~~~~~~~~~~~~~~~~~~~~
 
index 5c001594dc830b7db048ce981f01288e45411e37..6545a18a991c9d8c8ad85c036d7cc470957a7cb5 100644 (file)
@@ -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
@@ -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
 ----
 
index b6f81a4b0f5815d761008de60456e4774628ef53..7d2ca05d2a0856d414e9c033ebad01f0821850f8 100644 (file)
@@ -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()
index 96405e70afc0c11deb2925552e041f304ffea19a..383302bad8b549d7833d093befb7c87439b68e06 100644 (file)
@@ -508,14 +508,18 @@ class CmdLineTest(unittest.TestCase):
             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,
@@ -526,6 +530,14 @@ class CmdLineTest(unittest.TestCase):
         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))")
@@ -555,6 +567,7 @@ class CmdLineTest(unittest.TestCase):
                          "default::ResourceWarning "
                          "default::Warning")
 
+        # Memory allocator debug hooks
         try:
             import _testcapi
         except ImportError:
@@ -569,6 +582,7 @@ class CmdLineTest(unittest.TestCase):
                 alloc_name = "malloc_debug"
             self.assertEqual(out, alloc_name)
 
+        # Faulthandler
         try:
             import faulthandler
         except ImportError:
@@ -581,6 +595,7 @@ class CmdLineTest(unittest.TestCase):
     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:
@@ -621,6 +636,24 @@ class CmdLineTest(unittest.TestCase):
             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):
 
index 4b8fcb9540ffe584765efea33c55e3260c30259f..6346094ad08b4cc77a6d807161aaf09900f49127 100644 (file)
@@ -526,10 +526,12 @@ class SysModuleTest(unittest.TestCase):
         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))
 
diff --git a/Misc/NEWS.d/next/Library/2017-11-29-00-42-47.bpo-321010.-axD5l.rst b/Misc/NEWS.d/next/Library/2017-11-29-00-42-47.bpo-321010.-axD5l.rst
new file mode 100644 (file)
index 0000000..715a269
--- /dev/null
@@ -0,0 +1 @@
+Add :attr:`sys.flags.dev_mode` flag
index ec33b5f086f0f1b52601efe38c44f962ef8d9593..e9d524a1463627cf63405d5094a88983c5869a40 100644 (file)
@@ -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)
@@ -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";
index d786739188a816059525c91f5f247784d03114d9..64bc14e9c3d7339ac0db029ccfdacc820d51dbda 100644 (file)
@@ -1814,6 +1814,7 @@ static PyStructSequence_Field flags_fields[] = {
     {"quiet",                   "-q"},
     {"hash_randomization",      "-R"},
     {"isolated",                "-I"},
+    {"dev_mode",                "-X dev"},
     {0}
 };
 
@@ -1821,7 +1822,7 @@ static PyStructSequence_Desc flags_desc = {
     "sys.flags",        /* name */
     flags__doc__,       /* doc */
     flags_fields,       /* fields */
-    13
+    14
 };
 
 static PyObject*
@@ -1829,6 +1830,7 @@ make_flags(void)
 {
     int pos = 0;
     PyObject *seq;
+    _PyCoreConfig *core_config = &_PyGILState_GetInterpreterStateUnsafe()->core_config;
 
     seq = PyStructSequence_New(&FlagsType);
     if (seq == NULL)
@@ -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);