From 5c0c048f29eaee27620d8c42ad8011e7f73ba8bf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 2 Jul 2018 10:51:29 +0200 Subject: [PATCH 1/3] bpo-34008: Allow to call Py_Main() after Py_Initialize() Py_Main() can again be called after Py_Initialize(), as in Python 3.6. The new configuration is ignored, except of _PyMainInterpreterConfig.argv which is used to update sys.argv. --- Lib/test/test_embed.py | 8 +++++++ .../2018-07-02-10-58-11.bpo-34008.COewz-.rst | 1 + Modules/main.c | 11 ++++++--- Programs/_testembed.c | 16 +++++++++++++ Python/pylifecycle.c | 23 ++++++++++++++++--- 5 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2018-07-02-10-58-11.bpo-34008.COewz-.rst diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index f3b60433ccc131..024c3f99a85d41 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -238,6 +238,14 @@ def test_initialize_twice(self): self.assertEqual(out, '') self.assertEqual(err, '') + def test_initialize_pymain(self): + """ + bpo-34008: Calling Py_Main() after Py_Initialize() must not fail. + """ + out, err = self.run_embedded_interpreter("initialize_pymain") + self.assertEqual(out.rstrip(), "Py_Main() after Py_Initialize: sys.argv=['-c', 'arg2']") + self.assertEqual(err, '') + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C API/2018-07-02-10-58-11.bpo-34008.COewz-.rst b/Misc/NEWS.d/next/C API/2018-07-02-10-58-11.bpo-34008.COewz-.rst new file mode 100644 index 00000000000000..d9881b9945dfac --- /dev/null +++ b/Misc/NEWS.d/next/C API/2018-07-02-10-58-11.bpo-34008.COewz-.rst @@ -0,0 +1 @@ +Py_Main() can again be called after Py_Initialize(), as in Python 3.6. diff --git a/Modules/main.c b/Modules/main.c index 6067d5eccffdef..bbc97e1a4af496 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -2144,6 +2144,7 @@ config_init_module_search_paths(_PyCoreConfig *config) static _PyInitError config_init_path_config(_PyCoreConfig *config) { + /* FIXME: this method has side effect: modify _Py_path_config */ _PyInitError err = _PyPathConfig_Init(config); if (_Py_INIT_FAILED(err)) { return err; @@ -2696,9 +2697,13 @@ pymain_main(_PyMain *pymain) pymain_init_stdio(pymain); - pymain->err = _Py_InitializeCore(&pymain->config); - if (_Py_INIT_FAILED(pymain->err)) { - _Py_FatalInitError(pymain->err); + /* bpo-34008: For backward compatibility reasons, calling Py_Main() after + Py_Initialize() ignores the new configuration. */ + if (!_PyRuntime.initialized) { + pymain->err = _Py_InitializeCore(&pymain->config); + if (_Py_INIT_FAILED(pymain->err)) { + _Py_FatalInitError(pymain->err); + } } if (pymain_init_python_main(pymain) < 0) { diff --git a/Programs/_testembed.c b/Programs/_testembed.c index b8827f074b9cce..7d45ef9988a1ac 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -276,6 +276,21 @@ static int test_initialize_twice(void) return 0; } +static int test_initialize_pymain(void) +{ + wchar_t *argv[] = {L"PYTHON", L"-c", + L"import sys; print(f'Py_Main() after Py_Initialize: sys.argv={sys.argv}')", + L"arg2"}; + _testembed_Py_Initialize(); + + /* bpo-34008: Calling Py_Main() after Py_Initialize() must not crash */ + Py_Main(Py_ARRAY_LENGTH(argv), argv); + + Py_Finalize(); + + return 0; +} + /* ********************************************************* * List of test cases and the function that implements it. @@ -302,6 +317,7 @@ static struct TestCase TestCases[] = { { "pre_initialization_sys_options", test_pre_initialization_sys_options }, { "bpo20891", test_bpo20891 }, { "initialize_twice", test_initialize_twice }, + { "initialize_pymain", test_initialize_pymain }, { NULL, NULL } }; diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 9bf0ebd35d0dc1..64fa1e58f7d748 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -772,6 +772,22 @@ _Py_InitializeCore(const _PyCoreConfig *core_config) return _Py_INIT_OK(); } +/* Py_Initialize() has already been called: update the main interpreter + configuration. Example of bpo-34008: Py_Main() called after + Py_Initialize(). */ +static _PyInitError +_Py_ReconfigureMainInterpreter(PyInterpreterState *interp, + const _PyMainInterpreterConfig *config) +{ + if (config->argv != NULL) { + int res = PyDict_SetItemString(interp->sysdict, "argv", config->argv); + if (res < 0) { + return _Py_INIT_ERR("fail to set sys.argv"); + } + } + return _Py_INIT_OK(); +} + /* Update interpreter state based on supplied configuration settings * * After calling this function, most of the restrictions on the interpreter @@ -793,9 +809,6 @@ _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) if (!_PyRuntime.core_initialized) { return _Py_INIT_ERR("runtime core not initialized"); } - if (_PyRuntime.initialized) { - return _Py_INIT_ERR("main interpreter already initialized"); - } /* Get current thread state and interpreter pointer */ tstate = PyThreadState_GET(); @@ -810,6 +823,10 @@ _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config) return _Py_INIT_ERR("failed to copy main interpreter config"); } + if (_PyRuntime.initialized) { + return _Py_ReconfigureMainInterpreter(interp, config); + } + if (interp->core_config._disable_importlib) { /* Special mode for freeze_importlib: run with no import system * From 28345391a440e5d4b8b31fdec60cb831d4c9233b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Jul 2018 17:04:15 +0200 Subject: [PATCH 2/3] Remove FIXME comment --- Modules/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/main.c b/Modules/main.c index bbc97e1a4af496..1ff985517db774 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -2144,7 +2144,6 @@ config_init_module_search_paths(_PyCoreConfig *config) static _PyInitError config_init_path_config(_PyCoreConfig *config) { - /* FIXME: this method has side effect: modify _Py_path_config */ _PyInitError err = _PyPathConfig_Init(config); if (_Py_INIT_FAILED(err)) { return err; From b874c8514c3a9eb0337f8a4521c36f7e5205ef4c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Jul 2018 17:05:00 +0200 Subject: [PATCH 3/3] Fix indent --- Programs/_testembed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Programs/_testembed.c b/Programs/_testembed.c index 7d45ef9988a1ac..b1be682f7adc52 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -279,7 +279,7 @@ static int test_initialize_twice(void) static int test_initialize_pymain(void) { wchar_t *argv[] = {L"PYTHON", L"-c", - L"import sys; print(f'Py_Main() after Py_Initialize: sys.argv={sys.argv}')", + L"import sys; print(f'Py_Main() after Py_Initialize: sys.argv={sys.argv}')", L"arg2"}; _testembed_Py_Initialize();