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.
}
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,
def test_init_set_config(self):
config = {
- '_init_main': 0,
'bytes_warning': 2,
'warnoptions': ['error::BytesWarning'],
}
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);
return 1;
}
- // Finish initialization: main part
- PyStatus status = _Py_InitializeMain();
- if (PyStatus_Exception(status)) {
- Py_ExitStatusException(status);
- }
-
dump_config();
Py_Finalize();
return 0;
}
-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_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},