Skip to content

Commit 03ec4df

Browse files
bpo-34008: Allow to call Py_Main() after Py_Initialize() (GH-8043) (GH-8352)
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. (cherry picked from commit fb47bca) Co-authored-by: Victor Stinner <vstinner@redhat.com>
1 parent 49abd30 commit 03ec4df

File tree

5 files changed

+52
-6
lines changed

5 files changed

+52
-6
lines changed

‎Lib/test/test_embed.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,14 @@ def test_initialize_twice(self):
238238
self.assertEqual(out, '')
239239
self.assertEqual(err, '')
240240

241+
def test_initialize_pymain(self):
242+
"""
243+
bpo-34008: Calling Py_Main() after Py_Initialize() must not fail.
244+
"""
245+
out, err = self.run_embedded_interpreter("initialize_pymain")
246+
self.assertEqual(out.rstrip(), "Py_Main() after Py_Initialize: sys.argv=['-c', 'arg2']")
247+
self.assertEqual(err, '')
248+
241249

242250
if __name__ == "__main__":
243251
unittest.main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Py_Main() can again be called after Py_Initialize(), as in Python 3.6.

‎Modules/main.c‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2647,9 +2647,13 @@ pymain_main(_PyMain *pymain)
26472647

26482648
pymain_init_stdio(pymain);
26492649

2650-
pymain->err = _Py_InitializeCore(&pymain->config);
2651-
if (_Py_INIT_FAILED(pymain->err)) {
2652-
_Py_FatalInitError(pymain->err);
2650+
/* bpo-34008: For backward compatibility reasons, calling Py_Main() after
2651+
Py_Initialize() ignores the new configuration. */
2652+
if (!_PyRuntime.initialized) {
2653+
pymain->err = _Py_InitializeCore(&pymain->config);
2654+
if (_Py_INIT_FAILED(pymain->err)) {
2655+
_Py_FatalInitError(pymain->err);
2656+
}
26532657
}
26542658

26552659
if (pymain_init_python_main(pymain) < 0) {

‎Programs/_testembed.c‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,21 @@ static int test_initialize_twice(void)
276276
return 0;
277277
}
278278

279+
static int test_initialize_pymain(void)
280+
{
281+
wchar_t *argv[] = {L"PYTHON", L"-c",
282+
L"import sys; print(f'Py_Main() after Py_Initialize: sys.argv={sys.argv}')",
283+
L"arg2"};
284+
_testembed_Py_Initialize();
285+
286+
/* bpo-34008: Calling Py_Main() after Py_Initialize() must not crash */
287+
Py_Main(Py_ARRAY_LENGTH(argv), argv);
288+
289+
Py_Finalize();
290+
291+
return 0;
292+
}
293+
279294

280295
/* *********************************************************
281296
* List of test cases and the function that implements it.
@@ -302,6 +317,7 @@ static struct TestCase TestCases[] = {
302317
{ "pre_initialization_sys_options", test_pre_initialization_sys_options },
303318
{ "bpo20891", test_bpo20891 },
304319
{ "initialize_twice", test_initialize_twice },
320+
{ "initialize_pymain", test_initialize_pymain },
305321
{ NULL, NULL }
306322
};
307323

‎Python/pylifecycle.c‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,22 @@ _Py_InitializeCore(const _PyCoreConfig *core_config)
775775
return _Py_INIT_OK();
776776
}
777777

778+
/* Py_Initialize() has already been called: update the main interpreter
779+
configuration. Example of bpo-34008: Py_Main() called after
780+
Py_Initialize(). */
781+
static _PyInitError
782+
_Py_ReconfigureMainInterpreter(PyInterpreterState *interp,
783+
const _PyMainInterpreterConfig *config)
784+
{
785+
if (config->argv != NULL) {
786+
int res = PyDict_SetItemString(interp->sysdict, "argv", config->argv);
787+
if (res < 0) {
788+
return _Py_INIT_ERR("fail to set sys.argv");
789+
}
790+
}
791+
return _Py_INIT_OK();
792+
}
793+
778794
/* Update interpreter state based on supplied configuration settings
779795
*
780796
* After calling this function, most of the restrictions on the interpreter
@@ -796,9 +812,6 @@ _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
796812
if (!_PyRuntime.core_initialized) {
797813
return _Py_INIT_ERR("runtime core not initialized");
798814
}
799-
if (_PyRuntime.initialized) {
800-
return _Py_INIT_ERR("main interpreter already initialized");
801-
}
802815

803816
/* Get current thread state and interpreter pointer */
804817
tstate = PyThreadState_GET();
@@ -813,6 +826,10 @@ _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
813826
return _Py_INIT_ERR("failed to copy main interpreter config");
814827
}
815828

829+
if (_PyRuntime.initialized) {
830+
return _Py_ReconfigureMainInterpreter(interp, config);
831+
}
832+
816833
if (interp->core_config._disable_importlib) {
817834
/* Special mode for freeze_importlib: run with no import system
818835
*

0 commit comments

Comments
 (0)