Skip to content

bpo-36763: Add _Py_InitializeMain() #13362

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

Merged
merged 1 commit into from
May 16, 2019
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
13 changes: 9 additions & 4 deletions Include/cpython/coreconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,14 @@ typedef struct {
See PEP 552 "Deterministic pycs" for more details. */
wchar_t *check_hash_pycs_mode;

/* If greater than 0, suppress _PyPathConfig_Calculate() warnings.
/* If greater than 0, suppress _PyPathConfig_Calculate() warnings on Unix.
The parameter has no effect on Windows.

If set to -1 (default), inherit Py_FrozenFlag value. */
int _frozen;
If set to -1 (default), inherit !Py_FrozenFlag value. */
int pathconfig_warnings;

/* If equal to 0, stop Python initialization before the "main" phase */
int _init_main;

} _PyCoreConfig;

Expand Down Expand Up @@ -438,7 +442,8 @@ typedef struct {
.buffered_stdio = -1, \
._install_importlib = 1, \
.check_hash_pycs_mode = NULL, \
._frozen = -1}
.pathconfig_warnings = -1, \
._init_main = 1}
/* Note: _PyCoreConfig_INIT sets other fields to 0/NULL */

#ifdef __cplusplus
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ PyAPI_FUNC(_PyInitError) _Py_InitializeFromWideArgs(
const _PyCoreConfig *config,
int argc,
wchar_t **argv);
PyAPI_FUNC(_PyInitError) _Py_InitializeMain(void);

PyAPI_FUNC(int) _Py_RunMain(void);

Expand Down
43 changes: 31 additions & 12 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):

'_install_importlib': 1,
'check_hash_pycs_mode': 'default',
'_frozen': 0,
'pathconfig_warnings': 1,
'_init_main': 1,
}
if MS_WINDOWS:
DEFAULT_PRE_CONFIG.update({
Expand Down Expand Up @@ -371,7 +372,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
('Py_DontWriteBytecodeFlag', 'write_bytecode', True),
('Py_FileSystemDefaultEncodeErrors', 'filesystem_errors'),
('Py_FileSystemDefaultEncoding', 'filesystem_encoding'),
('Py_FrozenFlag', '_frozen'),
('Py_FrozenFlag', 'pathconfig_warnings', True),
('Py_IgnoreEnvironmentFlag', 'use_environment', True),
('Py_InspectFlag', 'inspect'),
('Py_InteractiveFlag', 'interactive'),
Expand Down Expand Up @@ -500,7 +501,8 @@ def check_global_config(self, config):

self.assertEqual(config['global_config'], expected)

def check_config(self, testname, expected_config, expected_preconfig, add_path=None):
def check_config(self, testname, expected_config, expected_preconfig,
add_path=None, stderr=None):
env = dict(os.environ)
# Remove PYTHON* environment variables to get deterministic environment
for key in list(env):
Expand All @@ -511,19 +513,22 @@ def check_config(self, testname, expected_config, expected_preconfig, add_path=N
env['PYTHONCOERCECLOCALE'] = '0'
env['PYTHONUTF8'] = '0'

out, err = self.run_embedded_interpreter(testname, env=env)
# Ignore err
try:
config = json.loads(out)
except json.JSONDecodeError:
self.fail(f"fail to decode stdout: {out!r}")

expected_preconfig = dict(self.DEFAULT_PRE_CONFIG, **expected_preconfig)
expected_config = self.get_expected_config(expected_config, env, add_path)
for key in self.COPY_PRE_CONFIG:
if key not in expected_preconfig:
expected_preconfig[key] = expected_config[key]

out, err = self.run_embedded_interpreter(testname, env=env)
if stderr is None and not expected_config['verbose']:
stderr = ""
if stderr is not None:
self.assertEqual(err.rstrip(), stderr)
try:
config = json.loads(out)
except json.JSONDecodeError:
self.fail(f"fail to decode stdout: {out!r}")

self.check_pre_config(config, expected_preconfig)
self.check_core_config(config, expected_config)
self.check_global_config(config)
Expand Down Expand Up @@ -689,7 +694,19 @@ def test_init_read_set(self):
self.check_config("init_read_set", core_config, preconfig,
add_path="init_read_set_path")

def test_run_main_config(self):
def test_init_run_main(self):
preconfig = {}
code = ('import _testinternalcapi, json; '
'print(json.dumps(_testinternalcapi.get_configs()))')
core_config = {
'argv': ['-c', 'arg2'],
'program': 'python3',
'program_name': './python3',
'run_command': code + '\n',
}
self.check_config("init_run_main", core_config, preconfig)

def test_init_main(self):
preconfig = {}
code = ('import _testinternalcapi, json; '
'print(json.dumps(_testinternalcapi.get_configs()))')
Expand All @@ -698,8 +715,10 @@ def test_run_main_config(self):
'program': 'python3',
'program_name': './python3',
'run_command': code + '\n',
'_init_main': 0,
}
self.check_config("run_main_config", core_config, preconfig)
self.check_config("init_main", core_config, preconfig,
stderr="Run Python code before _Py_InitializeMain")

def test_init_dont_parse_argv(self):
core_config = {
Expand Down
6 changes: 3 additions & 3 deletions Modules/getpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ calculate_prefix(const _PyCoreConfig *core_config,
}

if (!calculate->prefix_found) {
if (!core_config->_frozen) {
if (core_config->pathconfig_warnings) {
fprintf(stderr,
"Could not find platform independent libraries <prefix>\n");
}
Expand Down Expand Up @@ -681,7 +681,7 @@ calculate_exec_prefix(const _PyCoreConfig *core_config,
}

if (!calculate->exec_prefix_found) {
if (!core_config->_frozen) {
if (core_config->pathconfig_warnings) {
fprintf(stderr,
"Could not find platform dependent libraries <exec_prefix>\n");
}
Expand Down Expand Up @@ -1206,7 +1206,7 @@ calculate_path_impl(const _PyCoreConfig *core_config,
}

if ((!calculate->prefix_found || !calculate->exec_prefix_found) &&
!core_config->_frozen)
core_config->pathconfig_warnings)
{
fprintf(stderr,
"Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]\n");
Expand Down
3 changes: 2 additions & 1 deletion Programs/_freeze_importlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ main(int argc, char *argv[])
config.program_name = L"./_freeze_importlib";
/* Don't install importlib, since it could execute outdated bytecode. */
config._install_importlib = 0;
config._frozen = 1;
config.pathconfig_warnings = 0;
config._init_main = 0;

_PyInitError err = _Py_InitializeFromConfig(&config);
/* No need to call _PyCoreConfig_Clear() since we didn't allocate any
Expand Down
62 changes: 50 additions & 12 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -757,34 +757,71 @@ static int test_init_read_set(void)
}


static int test_run_main(void)
wchar_t *init_main_argv[] = {
L"python3", L"-c",
(L"import _testinternalcapi, json; "
L"print(json.dumps(_testinternalcapi.get_configs()))"),
L"arg2"};


static void configure_init_main(_PyCoreConfig *config)
{
config->argv.length = Py_ARRAY_LENGTH(init_main_argv);
config->argv.items = init_main_argv;
config->program_name = L"./python3";
}


static int test_init_run_main(void)
{
_PyCoreConfig config = _PyCoreConfig_INIT;
configure_init_main(&config);

_PyInitError err = _Py_InitializeFromConfig(&config);
if (_Py_INIT_FAILED(err)) {
_Py_ExitInitError(err);
}

return _Py_RunMain();
}

wchar_t *argv[] = {L"python3", L"-c",
(L"import sys; "
L"print(f'_Py_RunMain(): sys.argv={sys.argv}')"),
L"arg2"};
config.argv.length = Py_ARRAY_LENGTH(argv);
config.argv.items = argv;
config.program_name = L"./python3";

static int test_init_main(void)
{
_PyCoreConfig config = _PyCoreConfig_INIT;
configure_init_main(&config);
config._init_main = 0;

_PyInitError err = _Py_InitializeFromConfig(&config);
if (_Py_INIT_FAILED(err)) {
_Py_ExitInitError(err);
}

/* sys.stdout don't exist yet: it is created by _Py_InitializeMain() */
int res = PyRun_SimpleString(
"import sys; "
"print('Run Python code before _Py_InitializeMain', "
"file=sys.stderr)");
if (res < 0) {
exit(1);
}

err = _Py_InitializeMain();
if (_Py_INIT_FAILED(err)) {
_Py_ExitInitError(err);
}

return _Py_RunMain();
}


static int test_run_main_config(void)
static int test_run_main(void)
{
_PyCoreConfig config = _PyCoreConfig_INIT;

wchar_t *argv[] = {L"python3", L"-c",
(L"import _testinternalcapi, json; "
L"print(json.dumps(_testinternalcapi.get_configs()))"),
(L"import sys; "
L"print(f'_Py_RunMain(): sys.argv={sys.argv}')"),
L"arg2"};
config.argv.length = Py_ARRAY_LENGTH(argv);
config.argv.items = argv;
Expand Down Expand Up @@ -837,8 +874,9 @@ static struct TestCase TestCases[] = {
{ "preinit_isolated1", test_preinit_isolated1 },
{ "preinit_isolated2", test_preinit_isolated2 },
{ "init_read_set", test_init_read_set },
{ "init_run_main", test_init_run_main },
{ "init_main", test_init_main },
{ "run_main", test_run_main },
{ "run_main_config", test_run_main_config },
{ NULL, NULL }
};

Expand Down
12 changes: 7 additions & 5 deletions Python/coreconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,8 @@ _PyCoreConfig_Copy(_PyCoreConfig *config, const _PyCoreConfig *config2)
COPY_WSTR_ATTR(run_module);
COPY_WSTR_ATTR(run_filename);
COPY_WSTR_ATTR(check_hash_pycs_mode);
COPY_ATTR(_frozen);
COPY_ATTR(pathconfig_warnings);
COPY_ATTR(_init_main);

#undef COPY_ATTR
#undef COPY_WSTR_ATTR
Expand Down Expand Up @@ -766,7 +767,8 @@ _PyCoreConfig_AsDict(const _PyCoreConfig *config)
SET_ITEM_WSTR(run_filename);
SET_ITEM_INT(_install_importlib);
SET_ITEM_WSTR(check_hash_pycs_mode);
SET_ITEM_INT(_frozen);
SET_ITEM_INT(pathconfig_warnings);
SET_ITEM_INT(_init_main);

return dict;

Expand Down Expand Up @@ -855,7 +857,7 @@ _PyCoreConfig_GetGlobalConfig(_PyCoreConfig *config)
#ifdef MS_WINDOWS
COPY_FLAG(legacy_windows_stdio, Py_LegacyWindowsStdioFlag);
#endif
COPY_FLAG(_frozen, Py_FrozenFlag);
COPY_NOT_FLAG(pathconfig_warnings, Py_FrozenFlag);

COPY_NOT_FLAG(buffered_stdio, Py_UnbufferedStdioFlag);
COPY_NOT_FLAG(site_import, Py_NoSiteFlag);
Expand Down Expand Up @@ -892,7 +894,7 @@ _PyCoreConfig_SetGlobalConfig(const _PyCoreConfig *config)
#ifdef MS_WINDOWS
COPY_FLAG(legacy_windows_stdio, Py_LegacyWindowsStdioFlag);
#endif
COPY_FLAG(_frozen, Py_FrozenFlag);
COPY_NOT_FLAG(pathconfig_warnings, Py_FrozenFlag);

COPY_NOT_FLAG(buffered_stdio, Py_UnbufferedStdioFlag);
COPY_NOT_FLAG(site_import, Py_NoSiteFlag);
Expand Down Expand Up @@ -2253,7 +2255,7 @@ _PyCoreConfig_Read(_PyCoreConfig *config)
assert(!(config->run_command != NULL && config->run_module != NULL));
assert(config->check_hash_pycs_mode != NULL);
assert(config->_install_importlib >= 0);
assert(config->_frozen >= 0);
assert(config->pathconfig_warnings >= 0);

err = _Py_INIT_OK();

Expand Down
2 changes: 1 addition & 1 deletion Python/frozenmain.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Py_FrozenMain(int argc, char **argv)
}

_PyCoreConfig config = _PyCoreConfig_INIT;
config._frozen = 1; /* Suppress errors from getpath.c */
config.pathconfig_warnings = 0; /* Suppress errors from getpath.c */

if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\0')
inspect = 1;
Expand Down
17 changes: 16 additions & 1 deletion Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,21 @@ _Py_InitializeMainInterpreter(_PyRuntimeState *runtime,
return _Py_INIT_OK();
}


_PyInitError
_Py_InitializeMain(void)
{
_PyInitError err = _PyRuntime_Initialize();
if (_Py_INIT_FAILED(err)) {
return err;
}
_PyRuntimeState *runtime = &_PyRuntime;
PyInterpreterState *interp = _PyRuntimeState_GetThreadState(runtime)->interp;

return _Py_InitializeMainInterpreter(runtime, interp);
}


#undef _INIT_DEBUG_PRINT

static _PyInitError
Expand All @@ -990,7 +1005,7 @@ init_python(const _PyCoreConfig *config, const _PyArgv *args)
}
config = &interp->core_config;

if (!config->_frozen) {
if (config->_init_main) {
err = _Py_InitializeMainInterpreter(runtime, interp);
if (_Py_INIT_FAILED(err)) {
return err;
Expand Down
9 changes: 9 additions & 0 deletions Python/pythonrun.c
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,15 @@ run_eval_code_obj(PyCodeObject *co, PyObject *globals, PyObject *locals)
* Py_Main() based one.
*/
_Py_UnhandledKeyboardInterrupt = 0;

/* Set globals['__builtins__'] if it doesn't exist */
if (globals != NULL && PyDict_GetItemString(globals, "__builtins__") == NULL) {
PyInterpreterState *interp = _PyInterpreterState_Get();
if (PyDict_SetItemString(globals, "__builtins__", interp->builtins) < 0) {
return NULL;
}
}

v = PyEval_EvalCode((PyObject*)co, globals, locals);
if (!v && PyErr_Occurred() == PyExc_KeyboardInterrupt) {
_Py_UnhandledKeyboardInterrupt = 1;
Expand Down