]> granicus.if.org Git - python/commitdiff
bpo-31845: Fix reading flags from environment (GH-4105)
authorNick Coghlan <ncoghlan@gmail.com>
Wed, 25 Oct 2017 02:11:26 +0000 (12:11 +1000)
committerGitHub <noreply@github.com>
Wed, 25 Oct 2017 02:11:26 +0000 (12:11 +1000)
The startup refactoring means command line settings
are now applied after settings are read from the
environment.

This updates the way command line settings are applied
to account for that, ensures more settings are first read
from the environment in _PyInitializeCore, and adds a
simple test case covering the flags that are easy to check.

Lib/test/test_cmd_line.py
Misc/NEWS.d/next/Core and Builtins/2017-10-24-21-27-32.bpo-31845.8OS-k3.rst [new file with mode: 0644]
Modules/main.c
Python/pylifecycle.c

index 28ddb2ba1bf223d89be348db9045e4e8601dc0de..1b584ebb7c5ec33c5b011be527ba875cbdcd2ad9 100644 (file)
@@ -12,7 +12,6 @@ from test.support.script_helper import (
     spawn_python, kill_python, assert_python_ok, assert_python_failure
 )
 
-
 # XXX (ncoghlan): Move to script_helper and make consistent with run_python
 def _kill_python_and_exit_code(p):
     data = kill_python(p)
@@ -486,6 +485,26 @@ class CmdLineTest(unittest.TestCase):
                                           cwd=tmpdir)
             self.assertEqual(out.strip(), b"ok")
 
+    def test_sys_flags_set(self):
+        # Issue 31845: a startup refactoring broke reading flags from env vars
+        for value, expected in (("", 0), ("1", 1), ("text", 1), ("2", 2)):
+            env_vars = dict(
+                PYTHONDEBUG=value,
+                PYTHONOPTIMIZE=value,
+                PYTHONDONTWRITEBYTECODE=value,
+                PYTHONVERBOSE=value,
+            )
+            code = (
+                "import sys; "
+                "sys.stderr.write(str(sys.flags)); "
+                f"""sys.exit(not (
+                    sys.flags.debug == sys.flags.optimize ==
+                    sys.flags.dont_write_bytecode == sys.flags.verbose ==
+                    {expected}
+                ))"""
+            )
+            with self.subTest(envar_value=value):
+                assert_python_ok('-c', code, **env_vars)
 
 class IgnoreEnvironmentTest(unittest.TestCase):
 
@@ -506,6 +525,20 @@ class IgnoreEnvironmentTest(unittest.TestCase):
         self.run_ignoring_vars("sys.flags.hash_randomization == 1",
                                PYTHONHASHSEED="0")
 
+    def test_sys_flags_not_set(self):
+        # Issue 31845: a startup refactoring broke reading flags from env vars
+        expected_outcome = """
+            (sys.flags.debug == sys.flags.optimize ==
+             sys.flags.dont_write_bytecode == sys.flags.verbose == 0)
+        """
+        self.run_ignoring_vars(
+            expected_outcome,
+            PYTHONDEBUG="1",
+            PYTHONOPTIMIZE="1",
+            PYTHONDONTWRITEBYTECODE="1",
+            PYTHONVERBOSE="1",
+        )
+
 
 def test_main():
     test.support.run_unittest(CmdLineTest, IgnoreEnvironmentTest)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-10-24-21-27-32.bpo-31845.8OS-k3.rst b/Misc/NEWS.d/next/Core and Builtins/2017-10-24-21-27-32.bpo-31845.8OS-k3.rst
new file mode 100644 (file)
index 0000000..ddaf630
--- /dev/null
@@ -0,0 +1 @@
+Environment variables are once more read correctly at interpreter startup.
index e86211331047ee68898d2a2cbe38346aa58737ec..846ecb6170d86e7bf108e3c20ab2e36109954468 100644 (file)
@@ -522,39 +522,33 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
     return 0;
 }
 
-static int
-apply_command_line_and_environment(_Py_CommandLineDetails *cmdline)
+static void
+maybe_set_flag(int *flag, int value)
 {
-    char *p;
-    Py_BytesWarningFlag = cmdline->bytes_warning;
-    Py_DebugFlag = cmdline->debug;
-    Py_InspectFlag = cmdline->inspect;
-    Py_InteractiveFlag = cmdline->interactive;
-    Py_IsolatedFlag = cmdline->isolated;
-    Py_OptimizeFlag = cmdline->optimization_level;
-    Py_DontWriteBytecodeFlag = cmdline->dont_write_bytecode;
-    Py_NoUserSiteDirectory = cmdline->no_user_site_directory;
-    Py_NoSiteFlag = cmdline->no_site_import;
-    Py_UnbufferedStdioFlag = cmdline->use_unbuffered_io;
-    Py_VerboseFlag = cmdline->verbosity;
-    Py_QuietFlag = cmdline->quiet_flag;
-
-    if (!Py_InspectFlag &&
-        (p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') {
-        Py_InspectFlag = 1;
-        cmdline->inspect = 1;
-    }
-    if (!cmdline->use_unbuffered_io &&
-        (p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') {
-        Py_UnbufferedStdioFlag = 1;
-        cmdline->use_unbuffered_io = 1;
+    /* Helper to set flag variables from command line options
+    *   - uses the higher of the two values if they're both set
+    *   - otherwise leaves the flag unset
+    */
+    if (*flag < value) {
+        *flag = value;
     }
+}
 
-    if (!Py_NoUserSiteDirectory &&
-        (p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0') {
-        Py_NoUserSiteDirectory = 1;
-        cmdline->no_user_site_directory = 1;
-    }
+static int
+apply_command_line_and_environment(_Py_CommandLineDetails *cmdline)
+{
+    maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning);
+    maybe_set_flag(&Py_DebugFlag, cmdline->debug);
+    maybe_set_flag(&Py_InspectFlag, cmdline->inspect);
+    maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive);
+    maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated);
+    maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level);
+    maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode);
+    maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory);
+    maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import);
+    maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io);
+    maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity);
+    maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag);
 
     /* TODO: Apply PYTHONWARNINGS & -W options to sys module here */
     /* TODO: Apply -X options to sys module here */
index 5b13bc45822b2ad2acf899ccebe91a3e33257cd5..4e8bbb662e971e39c2015e340d9501617acb2ba9 100644 (file)
@@ -217,15 +217,18 @@ Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
 
 */
 
-static int
-add_flag(int flag, const char *envs)
+static void
+set_flag(int *flag, const char *envs)
 {
+    /* Helper to set flag variables from environment variables:
+    *   - uses the higher of the two values if they're both set
+    *   - otherwise sets the flag to 1
+    */
     int env = atoi(envs);
-    if (flag < env)
-        flag = env;
-    if (flag < 1)
-        flag = 1;
-    return flag;
+    if (*flag < env)
+        *flag = env;
+    if (*flag < 1)
+        *flag = 1;
 }
 
 static char*
@@ -612,22 +615,28 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
 #endif
 
     if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0')
-        Py_DebugFlag = add_flag(Py_DebugFlag, p);
+        set_flag(&Py_DebugFlag, p);
     if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0')
-        Py_VerboseFlag = add_flag(Py_VerboseFlag, p);
+        set_flag(&Py_VerboseFlag, p);
     if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0')
-        Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p);
+        set_flag(&Py_OptimizeFlag, p);
+    if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
+        set_flag(&Py_InspectFlag, p);
     if ((p = Py_GETENV("PYTHONDONTWRITEBYTECODE")) && *p != '\0')
-        Py_DontWriteBytecodeFlag = add_flag(Py_DontWriteBytecodeFlag, p);
+        set_flag(&Py_DontWriteBytecodeFlag, p);
+    if ((p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
+        set_flag(&Py_NoUserSiteDirectory, p);
+    if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
+        set_flag(&Py_UnbufferedStdioFlag, p);
     /* The variable is only tested for existence here;
        _Py_HashRandomization_Init will check its value further. */
     if ((p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0')
-        Py_HashRandomizationFlag = add_flag(Py_HashRandomizationFlag, p);
+        set_flag(&Py_HashRandomizationFlag, p);
 #ifdef MS_WINDOWS
     if ((p = Py_GETENV("PYTHONLEGACYWINDOWSFSENCODING")) && *p != '\0')
-        Py_LegacyWindowsFSEncodingFlag = add_flag(Py_LegacyWindowsFSEncodingFlag, p);
+        set_flag(&Py_LegacyWindowsFSEncodingFlag, p);
     if ((p = Py_GETENV("PYTHONLEGACYWINDOWSSTDIO")) && *p != '\0')
-        Py_LegacyWindowsStdioFlag = add_flag(Py_LegacyWindowsStdioFlag, p);
+        set_flag(&Py_LegacyWindowsStdioFlag, p);
 #endif
 
     _Py_HashRandomization_Init(&core_config);