diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 659c6df644e34f..927dcd7f243a4e 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -54,8 +54,9 @@ PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding, PyAPI_FUNC(_PyInitError) _Py_InitializeCore(const _PyCoreConfig *); PyAPI_FUNC(int) _Py_IsCoreInitialized(void); -PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *); +PyAPI_FUNC(_PyInitError) _PyCoreConfig_Read(_PyCoreConfig *, int); PyAPI_FUNC(void) _PyCoreConfig_Clear(_PyCoreConfig *); +PyAPI_FUNC(void) _PyCoreConfig_UpdateGlobals(_PyCoreConfig *); PyAPI_FUNC(int) _PyCoreConfig_Copy( _PyCoreConfig *config, const _PyCoreConfig *config2); diff --git a/Include/pystate.h b/Include/pystate.h index 29d7148bf9a3e9..99b6e26b5e222c 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -72,6 +72,12 @@ typedef struct { wchar_t *exec_prefix; /* sys.exec_prefix */ wchar_t *base_exec_prefix; /* sys.base_exec_prefix */ + int optimization_level; /* Py_OptimizeFlag, -O, PYTHONOPTIMIZE */ + int inspect; /* Py_InspectFlag, -i, PYTHONINSPECT */ + int no_user_site_directory; /* Py_NoUserSiteDirectory, -I, -s, PYTHONNOUSERSITE */ + int dont_write_bytecode; /* Py_DontWriteBytecodeFlag, -B, PYTHONDONTWRITEBYTECODE */ + int use_unbuffered_io; /* Py_UnbufferedStdioFlag, -u, PYTHONUNBUFFERED */ + /* Private fields */ int _disable_importlib; /* Needed by freeze_importlib */ } _PyCoreConfig; diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 024c3f99a85d41..831408a1714751 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -219,6 +219,47 @@ def test_pre_initialization_sys_options(self): self.assertIn(expected_output, out) self.assertEqual(err, '') + def test_initialize_sys_flags_from_environment(self): + """ + Validate that settings from environment variables affect + interpreter settings + """ + env = { k: os.environ[k] for k in os.environ + if not k.startswith('PYTHON') } + + out, err = self.run_embedded_interpreter( + "initialize_sys_flags", env=env) + + expected_output = ( + "dont_write_bytecode=0\n" + "hash_randomization=1\n" + "inspect=0\n" + "no_user_site=0\n" + "optimize=0\n" + ) + self.assertIn(expected_output, out) + self.assertEqual(err, '') + + env['PYTHONDONTWRITEBYTECODE'] = '1' + env['PYTHONHASHSEED'] = '0' + env['PYTHONINSPECT'] = '3' + env['PYTHONNOUSERSITE'] = '4' + env['PYTHONOPTIMIZE'] = '2' + + out, err = self.run_embedded_interpreter( + "initialize_sys_flags", env=env) + + expected_output = ( + "dont_write_bytecode=1\n" + "hash_randomization=0\n" + "inspect=3\n" + "no_user_site=4\n" + "optimize=2\n" + ) + self.assertEqual(expected_output, out) + self.assertEqual(err, '') + + def test_bpo20891(self): """ bpo-20891: Calling PyGILState_Ensure in a non-Python thread before diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-07-28-13-08-54.bpo-34247.3J_0ge.rst b/Misc/NEWS.d/next/Core and Builtins/2018-07-28-13-08-54.bpo-34247.3J_0ge.rst new file mode 100644 index 00000000000000..c098488be528e8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-07-28-13-08-54.bpo-34247.3J_0ge.rst @@ -0,0 +1,2 @@ +Don't ignore environment variables with command-line equivalents in +Py_Initialize. diff --git a/Modules/main.c b/Modules/main.c index 31ebbceb83e061..cd0d91ac343a57 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -440,14 +440,9 @@ typedef struct { int print_version; /* -V option */ int bytes_warning; /* Py_BytesWarningFlag, -b */ int debug; /* Py_DebugFlag, -b, PYTHONDEBUG */ - int inspect; /* Py_InspectFlag, -i, PYTHONINSPECT */ int interactive; /* Py_InteractiveFlag, -i */ int isolated; /* Py_IsolatedFlag, -I */ - int optimization_level; /* Py_OptimizeFlag, -O, PYTHONOPTIMIZE */ - int dont_write_bytecode; /* Py_DontWriteBytecodeFlag, -B, PYTHONDONTWRITEBYTECODE */ - int no_user_site_directory; /* Py_NoUserSiteDirectory, -I, -s, PYTHONNOUSERSITE */ int no_site_import; /* Py_NoSiteFlag, -S */ - int use_unbuffered_io; /* Py_UnbufferedStdioFlag, -u, PYTHONUNBUFFERED */ int verbosity; /* Py_VerboseFlag, -v, PYTHONVERBOSE */ int quiet_flag; /* Py_QuietFlag, -q */ const char *check_hash_pycs_mode; /* --check-hash-based-pycs */ @@ -805,28 +800,28 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline) break; case 'i': - cmdline->inspect++; + config->inspect++; cmdline->interactive++; break; case 'I': config->ignore_environment++; cmdline->isolated++; - cmdline->no_user_site_directory++; + config->no_user_site_directory++; break; /* case 'J': reserved for Jython */ case 'O': - cmdline->optimization_level++; + config->optimization_level++; break; case 'B': - cmdline->dont_write_bytecode++; + config->dont_write_bytecode++; break; case 's': - cmdline->no_user_site_directory++; + config->no_user_site_directory++; break; case 'S': @@ -842,7 +837,7 @@ pymain_parse_cmdline_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline) break; case 'u': - cmdline->use_unbuffered_io = 1; + config->use_unbuffered_io = 1; break; case 'v': @@ -1368,14 +1363,14 @@ pymain_get_global_config(_PyMain *pymain, _Py_CommandLineDetails *cmdline) { cmdline->bytes_warning = Py_BytesWarningFlag; cmdline->debug = Py_DebugFlag; - cmdline->inspect = Py_InspectFlag; + pymain->config.inspect = Py_InspectFlag; cmdline->interactive = Py_InteractiveFlag; cmdline->isolated = Py_IsolatedFlag; - cmdline->optimization_level = Py_OptimizeFlag; - cmdline->dont_write_bytecode = Py_DontWriteBytecodeFlag; - cmdline->no_user_site_directory = Py_NoUserSiteDirectory; + pymain->config.optimization_level = Py_OptimizeFlag; + pymain->config.dont_write_bytecode = Py_DontWriteBytecodeFlag; + pymain->config.no_user_site_directory = Py_NoUserSiteDirectory; cmdline->no_site_import = Py_NoSiteFlag; - cmdline->use_unbuffered_io = Py_UnbufferedStdioFlag; + pymain->config.use_unbuffered_io = Py_UnbufferedStdioFlag; cmdline->verbosity = Py_VerboseFlag; cmdline->quiet_flag = Py_QuietFlag; #ifdef MS_WINDOWS @@ -1395,14 +1390,9 @@ pymain_set_global_config(_PyMain *pymain, _Py_CommandLineDetails *cmdline) { 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; _Py_CheckHashBasedPycsMode = cmdline->check_hash_pycs_mode; @@ -1411,15 +1401,11 @@ pymain_set_global_config(_PyMain *pymain, _Py_CommandLineDetails *cmdline) Py_LegacyWindowsStdioFlag = cmdline->legacy_windows_stdio; #endif - Py_IgnoreEnvironmentFlag = pymain->config.ignore_environment; - Py_UTF8Mode = pymain->config.utf8_mode; - - /* Random or non-zero hash seed */ - Py_HashRandomizationFlag = (pymain->config.use_hash_seed == 0 || - pymain->config.hash_seed != 0); + _PyCoreConfig_UpdateGlobals(&(pymain->config)); } + static void pymain_import_readline(_PyMain *pymain) { @@ -1694,15 +1680,15 @@ get_env_flag(int *flag, const char *name) static void -cmdline_get_env_flags(_Py_CommandLineDetails *cmdline) +cmdline_get_env_flags(_PyCoreConfig* config, _Py_CommandLineDetails *cmdline) { get_env_flag(&cmdline->debug, "PYTHONDEBUG"); get_env_flag(&cmdline->verbosity, "PYTHONVERBOSE"); - get_env_flag(&cmdline->optimization_level, "PYTHONOPTIMIZE"); - get_env_flag(&cmdline->inspect, "PYTHONINSPECT"); - get_env_flag(&cmdline->dont_write_bytecode, "PYTHONDONTWRITEBYTECODE"); - get_env_flag(&cmdline->no_user_site_directory, "PYTHONNOUSERSITE"); - get_env_flag(&cmdline->use_unbuffered_io, "PYTHONUNBUFFERED"); + get_env_flag(&config->optimization_level, "PYTHONOPTIMIZE"); + get_env_flag(&config->inspect, "PYTHONINSPECT"); + get_env_flag(&config->dont_write_bytecode, "PYTHONDONTWRITEBYTECODE"); + get_env_flag(&config->no_user_site_directory, "PYTHONNOUSERSITE"); + get_env_flag(&config->use_unbuffered_io, "PYTHONUNBUFFERED"); #ifdef MS_WINDOWS get_env_flag(&cmdline->legacy_windows_fs_encoding, "PYTHONLEGACYWINDOWSFSENCODING"); @@ -1804,8 +1790,15 @@ config_init_utf8_mode(_PyCoreConfig *config) static _PyInitError -config_read_env_vars(_PyCoreConfig *config) +config_read_env_vars(_PyCoreConfig *config, int ignore_cmdline) { + if (!ignore_cmdline) { + get_env_flag(&config->optimization_level, "PYTHONOPTIMIZE"); + get_env_flag(&config->inspect, "PYTHONINSPECT"); + get_env_flag(&config->no_user_site_directory, "PYTHONNOUSERSITE"); + get_env_flag(&config->dont_write_bytecode, "PYTHONDONTWRITEBYTECODE"); + } + config->allocator = config_get_env_var("PYTHONMALLOC"); if (config_get_env_var("PYTHONDUMPREFS")) { @@ -1893,7 +1886,7 @@ pymain_read_conf_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline) Py_IgnoreEnvironmentFlag = config->ignore_environment; /* Get environment variables */ - cmdline_get_env_flags(cmdline); + cmdline_get_env_flags(config, cmdline); err = cmdline_init_env_warnoptions(cmdline); if (_Py_INIT_FAILED(err)) { @@ -1918,7 +1911,7 @@ pymain_read_conf_impl(_PyMain *pymain, _Py_CommandLineDetails *cmdline) Py_IsolatedFlag = cmdline->isolated; Py_NoSiteFlag = cmdline->no_site_import; - err = _PyCoreConfig_Read(config); + err = _PyCoreConfig_Read(config, 1); cmdline->isolated = Py_IsolatedFlag; cmdline->no_site_import = Py_NoSiteFlag; @@ -2168,12 +2161,28 @@ config_init_path_config(_PyCoreConfig *config) * this function multiple times with various preconfigured settings. */ +void +_PyCoreConfig_UpdateGlobals(_PyCoreConfig* config) +{ + Py_InspectFlag = config->inspect; + Py_OptimizeFlag = config->optimization_level; + Py_DontWriteBytecodeFlag = config->dont_write_bytecode; + Py_NoUserSiteDirectory = config->no_user_site_directory; + Py_UnbufferedStdioFlag = config->use_unbuffered_io; + + Py_IgnoreEnvironmentFlag = config->ignore_environment; + Py_UTF8Mode = config->utf8_mode; + + Py_HashRandomizationFlag = (config->use_hash_seed == 0 || + config->hash_seed != 0); +} + _PyInitError -_PyCoreConfig_Read(_PyCoreConfig *config) +_PyCoreConfig_Read(_PyCoreConfig *config, int ignore_cmdline) { _PyInitError err; - err = config_read_env_vars(config); + err = config_read_env_vars(config, ignore_cmdline); if (_Py_INIT_FAILED(err)) { return err; } diff --git a/Programs/_testembed.c b/Programs/_testembed.c index b1be682f7adc52..04f4e9805319a5 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -291,6 +291,24 @@ static int test_initialize_pymain(void) return 0; } +static int test_initialize_sys_flags(void) +{ + _testembed_Py_Initialize(); + + PyRun_SimpleString( + "import sys;" + "print(f\"dont_write_bytecode={sys.flags.dont_write_bytecode}\");" + "print(f\"hash_randomization={sys.flags.hash_randomization}\");" + "print(f\"inspect={sys.flags.inspect}\");" + "print(f\"no_user_site={sys.flags.no_user_site}\");" + "print(f\"optimize={sys.flags.optimize}\");" + "sys.stdout.flush()" + ); + + Py_Finalize(); + return 0; +} + /* ********************************************************* * List of test cases and the function that implements it. @@ -318,6 +336,7 @@ static struct TestCase TestCases[] = { { "bpo20891", test_bpo20891 }, { "initialize_twice", test_initialize_twice }, { "initialize_pymain", test_initialize_pymain }, + { "initialize_sys_flags", test_initialize_sys_flags }, { NULL, NULL } }; diff --git a/Python/pathconfig.c b/Python/pathconfig.c index 07aa01c0304f2c..080484ff356fee 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -108,7 +108,7 @@ pathconfig_global_init(void) _PyInitError err; _PyCoreConfig config = _PyCoreConfig_INIT; - err = _PyCoreConfig_Read(&config); + err = _PyCoreConfig_Read(&config, 1); if (_Py_INIT_FAILED(err)) { goto error; } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 219a46558825bc..9a917e058a09b4 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -924,11 +924,13 @@ _Py_InitializeEx_Private(int install_sigs, int install_importlib) config._disable_importlib = !install_importlib; config.install_signal_handlers = install_sigs; - err = _PyCoreConfig_Read(&config); + err = _PyCoreConfig_Read(&config, 0); if (_Py_INIT_FAILED(err)) { goto done; } + _PyCoreConfig_UpdateGlobals(&config); + err = _Py_InitializeCore(&config); if (_Py_INIT_FAILED(err)) { goto done; @@ -944,6 +946,7 @@ _Py_InitializeEx_Private(int install_sigs, int install_importlib) goto done; } + err = _Py_INIT_OK(); done: