Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-31845: Fix reading flags from environment #4105

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion Lib/test/test_cmd_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
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)
Expand Down Expand Up @@ -486,6 +485,26 @@ def test_isolatedmode(self):
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):

Expand All @@ -506,6 +525,20 @@ def test_ignore_PYTHONHASHSEED(self):
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Environment variables are once more read correctly at interpreter startup.
54 changes: 24 additions & 30 deletions Modules/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
37 changes: 23 additions & 14 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down Expand Up @@ -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);
Expand Down