Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d7ac061

Browse files
authored
bpo-31845: Fix reading flags from environment (pythonGH-4105)
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.
1 parent 850a18e commit d7ac061

File tree

4 files changed

+82
-45
lines changed

4 files changed

+82
-45
lines changed

Lib/test/test_cmd_line.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
spawn_python, kill_python, assert_python_ok, assert_python_failure
1313
)
1414

15-
1615
# XXX (ncoghlan): Move to script_helper and make consistent with run_python
1716
def _kill_python_and_exit_code(p):
1817
data = kill_python(p)
@@ -486,6 +485,26 @@ def test_isolatedmode(self):
486485
cwd=tmpdir)
487486
self.assertEqual(out.strip(), b"ok")
488487

488+
def test_sys_flags_set(self):
489+
# Issue 31845: a startup refactoring broke reading flags from env vars
490+
for value, expected in (("", 0), ("1", 1), ("text", 1), ("2", 2)):
491+
env_vars = dict(
492+
PYTHONDEBUG=value,
493+
PYTHONOPTIMIZE=value,
494+
PYTHONDONTWRITEBYTECODE=value,
495+
PYTHONVERBOSE=value,
496+
)
497+
code = (
498+
"import sys; "
499+
"sys.stderr.write(str(sys.flags)); "
500+
f"""sys.exit(not (
501+
sys.flags.debug == sys.flags.optimize ==
502+
sys.flags.dont_write_bytecode == sys.flags.verbose ==
503+
{expected}
504+
))"""
505+
)
506+
with self.subTest(envar_value=value):
507+
assert_python_ok('-c', code, **env_vars)
489508

490509
class IgnoreEnvironmentTest(unittest.TestCase):
491510

@@ -506,6 +525,20 @@ def test_ignore_PYTHONHASHSEED(self):
506525
self.run_ignoring_vars("sys.flags.hash_randomization == 1",
507526
PYTHONHASHSEED="0")
508527

528+
def test_sys_flags_not_set(self):
529+
# Issue 31845: a startup refactoring broke reading flags from env vars
530+
expected_outcome = """
531+
(sys.flags.debug == sys.flags.optimize ==
532+
sys.flags.dont_write_bytecode == sys.flags.verbose == 0)
533+
"""
534+
self.run_ignoring_vars(
535+
expected_outcome,
536+
PYTHONDEBUG="1",
537+
PYTHONOPTIMIZE="1",
538+
PYTHONDONTWRITEBYTECODE="1",
539+
PYTHONVERBOSE="1",
540+
)
541+
509542

510543
def test_main():
511544
test.support.run_unittest(CmdLineTest, IgnoreEnvironmentTest)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Environment variables are once more read correctly at interpreter startup.

Modules/main.c

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -522,39 +522,33 @@ read_command_line(int argc, wchar_t **argv, _Py_CommandLineDetails *cmdline)
522522
return 0;
523523
}
524524

525-
static int
526-
apply_command_line_and_environment(_Py_CommandLineDetails *cmdline)
525+
static void
526+
maybe_set_flag(int *flag, int value)
527527
{
528-
char *p;
529-
Py_BytesWarningFlag = cmdline->bytes_warning;
530-
Py_DebugFlag = cmdline->debug;
531-
Py_InspectFlag = cmdline->inspect;
532-
Py_InteractiveFlag = cmdline->interactive;
533-
Py_IsolatedFlag = cmdline->isolated;
534-
Py_OptimizeFlag = cmdline->optimization_level;
535-
Py_DontWriteBytecodeFlag = cmdline->dont_write_bytecode;
536-
Py_NoUserSiteDirectory = cmdline->no_user_site_directory;
537-
Py_NoSiteFlag = cmdline->no_site_import;
538-
Py_UnbufferedStdioFlag = cmdline->use_unbuffered_io;
539-
Py_VerboseFlag = cmdline->verbosity;
540-
Py_QuietFlag = cmdline->quiet_flag;
541-
542-
if (!Py_InspectFlag &&
543-
(p = Py_GETENV("PYTHONINSPECT")) && *p != '\0') {
544-
Py_InspectFlag = 1;
545-
cmdline->inspect = 1;
546-
}
547-
if (!cmdline->use_unbuffered_io &&
548-
(p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0') {
549-
Py_UnbufferedStdioFlag = 1;
550-
cmdline->use_unbuffered_io = 1;
528+
/* Helper to set flag variables from command line options
529+
* - uses the higher of the two values if they're both set
530+
* - otherwise leaves the flag unset
531+
*/
532+
if (*flag < value) {
533+
*flag = value;
551534
}
535+
}
552536

553-
if (!Py_NoUserSiteDirectory &&
554-
(p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0') {
555-
Py_NoUserSiteDirectory = 1;
556-
cmdline->no_user_site_directory = 1;
557-
}
537+
static int
538+
apply_command_line_and_environment(_Py_CommandLineDetails *cmdline)
539+
{
540+
maybe_set_flag(&Py_BytesWarningFlag, cmdline->bytes_warning);
541+
maybe_set_flag(&Py_DebugFlag, cmdline->debug);
542+
maybe_set_flag(&Py_InspectFlag, cmdline->inspect);
543+
maybe_set_flag(&Py_InteractiveFlag, cmdline->interactive);
544+
maybe_set_flag(&Py_IsolatedFlag, cmdline->isolated);
545+
maybe_set_flag(&Py_OptimizeFlag, cmdline->optimization_level);
546+
maybe_set_flag(&Py_DontWriteBytecodeFlag, cmdline->dont_write_bytecode);
547+
maybe_set_flag(&Py_NoUserSiteDirectory, cmdline->no_user_site_directory);
548+
maybe_set_flag(&Py_NoSiteFlag, cmdline->no_site_import);
549+
maybe_set_flag(&Py_UnbufferedStdioFlag, cmdline->use_unbuffered_io);
550+
maybe_set_flag(&Py_VerboseFlag, cmdline->verbosity);
551+
maybe_set_flag(&Py_QuietFlag, cmdline->quiet_flag);
558552

559553
/* TODO: Apply PYTHONWARNINGS & -W options to sys module here */
560554
/* TODO: Apply -X options to sys module here */

Python/pylifecycle.c

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,18 @@ Py_SetStandardStreamEncoding(const char *encoding, const char *errors)
217217
218218
*/
219219

220-
static int
221-
add_flag(int flag, const char *envs)
220+
static void
221+
set_flag(int *flag, const char *envs)
222222
{
223+
/* Helper to set flag variables from environment variables:
224+
* - uses the higher of the two values if they're both set
225+
* - otherwise sets the flag to 1
226+
*/
223227
int env = atoi(envs);
224-
if (flag < env)
225-
flag = env;
226-
if (flag < 1)
227-
flag = 1;
228-
return flag;
228+
if (*flag < env)
229+
*flag = env;
230+
if (*flag < 1)
231+
*flag = 1;
229232
}
230233

231234
static char*
@@ -612,22 +615,28 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
612615
#endif
613616

614617
if ((p = Py_GETENV("PYTHONDEBUG")) && *p != '\0')
615-
Py_DebugFlag = add_flag(Py_DebugFlag, p);
618+
set_flag(&Py_DebugFlag, p);
616619
if ((p = Py_GETENV("PYTHONVERBOSE")) && *p != '\0')
617-
Py_VerboseFlag = add_flag(Py_VerboseFlag, p);
620+
set_flag(&Py_VerboseFlag, p);
618621
if ((p = Py_GETENV("PYTHONOPTIMIZE")) && *p != '\0')
619-
Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p);
622+
set_flag(&Py_OptimizeFlag, p);
623+
if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
624+
set_flag(&Py_InspectFlag, p);
620625
if ((p = Py_GETENV("PYTHONDONTWRITEBYTECODE")) && *p != '\0')
621-
Py_DontWriteBytecodeFlag = add_flag(Py_DontWriteBytecodeFlag, p);
626+
set_flag(&Py_DontWriteBytecodeFlag, p);
627+
if ((p = Py_GETENV("PYTHONNOUSERSITE")) && *p != '\0')
628+
set_flag(&Py_NoUserSiteDirectory, p);
629+
if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\0')
630+
set_flag(&Py_UnbufferedStdioFlag, p);
622631
/* The variable is only tested for existence here;
623632
_Py_HashRandomization_Init will check its value further. */
624633
if ((p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0')
625-
Py_HashRandomizationFlag = add_flag(Py_HashRandomizationFlag, p);
634+
set_flag(&Py_HashRandomizationFlag, p);
626635
#ifdef MS_WINDOWS
627636
if ((p = Py_GETENV("PYTHONLEGACYWINDOWSFSENCODING")) && *p != '\0')
628-
Py_LegacyWindowsFSEncodingFlag = add_flag(Py_LegacyWindowsFSEncodingFlag, p);
637+
set_flag(&Py_LegacyWindowsFSEncodingFlag, p);
629638
if ((p = Py_GETENV("PYTHONLEGACYWINDOWSSTDIO")) && *p != '\0')
630-
Py_LegacyWindowsStdioFlag = add_flag(Py_LegacyWindowsStdioFlag, p);
639+
set_flag(&Py_LegacyWindowsStdioFlag, p);
631640
#endif
632641

633642
_Py_HashRandomization_Init(&core_config);

0 commit comments

Comments
 (0)