Skip to content

Commit

Permalink
pythongh-129033: Remove _Py_InitializeMain() function (python#129034)
Browse files Browse the repository at this point in the history
Co-authored-by: Alyssa Coghlan <[email protected]>
  • Loading branch information
vstinner and ncoghlan authored Jan 20, 2025
1 parent c463270 commit 07c3518
Show file tree
Hide file tree
Showing 7 changed files with 15 additions and 153 deletions.
92 changes: 8 additions & 84 deletions Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1946,89 +1946,13 @@ Py_GetArgcArgv()
See also :c:member:`PyConfig.orig_argv` member.
Delaying main module execution
==============================
Multi-Phase Initialization Private Provisional API
==================================================
In some embedding use cases, it may be desirable to separate interpreter initialization
from the execution of the main module.
This section is a private provisional API introducing multi-phase
initialization, the core feature of :pep:`432`:
* "Core" initialization phase, "bare minimum Python":
* Builtin types;
* Builtin exceptions;
* Builtin and frozen modules;
* The :mod:`sys` module is only partially initialized
(ex: :data:`sys.path` doesn't exist yet).
* "Main" initialization phase, Python is fully initialized:
* Install and configure :mod:`importlib`;
* Apply the :ref:`Path Configuration <init-path-config>`;
* Install signal handlers;
* Finish :mod:`sys` module initialization (ex: create :data:`sys.stdout`
and :data:`sys.path`);
* Enable optional features like :mod:`faulthandler` and :mod:`tracemalloc`;
* Import the :mod:`site` module;
* etc.
Private provisional API:
* :c:member:`PyConfig._init_main`: if set to ``0``,
:c:func:`Py_InitializeFromConfig` stops at the "Core" initialization phase.
.. c:function:: PyStatus _Py_InitializeMain(void)
Move to the "Main" initialization phase, finish the Python initialization.
No module is imported during the "Core" phase and the ``importlib`` module is
not configured: the :ref:`Path Configuration <init-path-config>` is only
applied during the "Main" phase. It may allow to customize Python in Python to
override or tune the :ref:`Path Configuration <init-path-config>`, maybe
install a custom :data:`sys.meta_path` importer or an import hook, etc.
It may become possible to calculate the :ref:`Path Configuration
<init-path-config>` in Python, after the Core phase and before the Main phase,
which is one of the :pep:`432` motivation.
The "Core" phase is not properly defined: what should be and what should
not be available at this phase is not specified yet. The API is marked
as private and provisional: the API can be modified or even be removed
anytime until a proper public API is designed.
Example running Python code between "Core" and "Main" initialization
phases::
void init_python(void)
{
PyStatus status;
PyConfig config;
PyConfig_InitPythonConfig(&config);
config._init_main = 0;
/* ... customize 'config' configuration ... */
status = Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
/* Use sys.stderr because sys.stdout is only 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);
}
/* ... put more configuration code here ... */
status = _Py_InitializeMain();
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}
}
This separation can be achieved by setting ``PyConfig.run_command`` to the empty
string during initialization (to prevent the interpreter from dropping into the
interactive prompt), and then subsequently executing the desired main module
code using ``__main__.__dict__`` as the global namespace.
4 changes: 4 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1375,3 +1375,7 @@ Removed

* Creating :c:data:`immutable types <Py_TPFLAGS_IMMUTABLETYPE>` with mutable
bases was deprecated since 3.12 and now raises a :exc:`TypeError`.

* Remove the private ``_Py_InitializeMain()`` function. It was a
:term:`provisional API` added to Python 3.8 by :pep:`587`.
(Contributed by Victor Stinner in :gh:`129033`.)
3 changes: 0 additions & 3 deletions Include/cpython/pylifecycle.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ PyAPI_FUNC(PyStatus) Py_PreInitializeFromArgs(
PyAPI_FUNC(PyStatus) Py_InitializeFromConfig(
const PyConfig *config);

// Python 3.8 provisional API (PEP 587)
PyAPI_FUNC(PyStatus) _Py_InitializeMain(void);

PyAPI_FUNC(int) Py_RunMain(void);


Expand Down
19 changes: 0 additions & 19 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -1274,24 +1274,6 @@ def test_init_run_main(self):
}
self.check_all_configs("test_init_run_main", config, api=API_PYTHON)

def test_init_main(self):
code = ('import _testinternalcapi, json; '
'print(json.dumps(_testinternalcapi.get_configs()))')
config = {
'argv': ['-c', 'arg2'],
'orig_argv': ['python3',
'-c', code,
'arg2'],
'program_name': './python3',
'run_command': code + '\n',
'parse_argv': True,
'_init_main': False,
'sys_path_0': '',
}
self.check_all_configs("test_init_main", config,
api=API_PYTHON,
stderr="Run Python code before _Py_InitializeMain")

def test_init_parse_argv(self):
config = {
'parse_argv': True,
Expand Down Expand Up @@ -1768,7 +1750,6 @@ def test_init_warnoptions(self):

def test_init_set_config(self):
config = {
'_init_main': 0,
'bytes_warning': 2,
'warnoptions': ['error::BytesWarning'],
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Remove the private ``_Py_InitializeMain()`` function. It was a
:term:`provisional API` added to Python 3.8 by :pep:`587`. Patch by Victor
Stinner.
35 changes: 0 additions & 35 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -1818,7 +1818,6 @@ static int test_init_set_config(void)
PyConfig config;
PyConfig_InitIsolatedConfig(&config);
config_set_string(&config, &config.program_name, PROGRAM_NAME);
config._init_main = 0;
config.bytes_warning = 0;
init_from_config_clear(&config);

Expand All @@ -1828,12 +1827,6 @@ static int test_init_set_config(void)
return 1;
}

// Finish initialization: main part
PyStatus status = _Py_InitializeMain();
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}

dump_config();
Py_Finalize();
return 0;
Expand Down Expand Up @@ -2089,33 +2082,6 @@ static int test_init_run_main(void)
}


static int test_init_main(void)
{
PyConfig config;
PyConfig_InitPythonConfig(&config);

configure_init_main(&config);
config._init_main = 0;
init_from_config_clear(&config);

/* 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);
}

PyStatus status = _Py_InitializeMain();
if (PyStatus_Exception(status)) {
Py_ExitStatusException(status);
}

return Py_RunMain();
}


static int test_run_main(void)
{
PyConfig config;
Expand Down Expand Up @@ -2473,7 +2439,6 @@ static struct TestCase TestCases[] = {
{"test_preinit_dont_parse_argv", test_preinit_dont_parse_argv},
{"test_init_read_set", test_init_read_set},
{"test_init_run_main", test_init_run_main},
{"test_init_main", test_init_main},
{"test_init_sys_add", test_init_sys_add},
{"test_init_setpath", test_init_setpath},
{"test_init_setpath_config", test_init_setpath_config},
Expand Down
12 changes: 0 additions & 12 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -1505,18 +1505,6 @@ Py_Initialize(void)
}


PyStatus
_Py_InitializeMain(void)
{
PyStatus status = _PyRuntime_Initialize();
if (_PyStatus_EXCEPTION(status)) {
return status;
}
PyThreadState *tstate = _PyThreadState_GET();
return pyinit_main(tstate);
}


static void
finalize_modules_delete_special(PyThreadState *tstate, int verbose)
{
Expand Down

0 comments on commit 07c3518

Please sign in to comment.