See also :c:member:`PyConfig.orig_argv` member.
-Delaying main module execution
-==============================
-In some embedding use cases, it may be desirable to separate interpreter initialization
-from the execution of the main module.
+Multi-Phase Initialization Private Provisional API
+==================================================
-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.
+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:: int 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);
+ }
+ }
}
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,
}
+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;
{"test_preinit_parse_argv", test_preinit_parse_argv},
{"test_preinit_dont_parse_argv", test_preinit_dont_parse_argv},
{"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},