Skip to content

Commit d1f507a

Browse files
Merge branch 'main' into Optimize-_MATCH_CLASS
2 parents 1d9fe86 + 18aec59 commit d1f507a

File tree

12 files changed

+167
-11
lines changed

12 files changed

+167
-11
lines changed

Doc/c-api/init_config.rst

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2299,13 +2299,91 @@ Py_GetArgcArgv()
22992299
23002300
See also :c:member:`PyConfig.orig_argv` member.
23012301
2302-
Delaying main module execution
2303-
==============================
23042302
2305-
In some embedding use cases, it may be desirable to separate interpreter initialization
2306-
from the execution of the main module.
2303+
Multi-Phase Initialization Private Provisional API
2304+
==================================================
23072305
2308-
This separation can be achieved by setting ``PyConfig.run_command`` to the empty
2309-
string during initialization (to prevent the interpreter from dropping into the
2310-
interactive prompt), and then subsequently executing the desired main module
2311-
code using ``__main__.__dict__`` as the global namespace.
2306+
This section is a private provisional API introducing multi-phase
2307+
initialization, the core feature of :pep:`432`:
2308+
2309+
* "Core" initialization phase, "bare minimum Python":
2310+
2311+
* Builtin types;
2312+
* Builtin exceptions;
2313+
* Builtin and frozen modules;
2314+
* The :mod:`sys` module is only partially initialized
2315+
(ex: :data:`sys.path` doesn't exist yet).
2316+
2317+
* "Main" initialization phase, Python is fully initialized:
2318+
2319+
* Install and configure :mod:`importlib`;
2320+
* Apply the :ref:`Path Configuration <init-path-config>`;
2321+
* Install signal handlers;
2322+
* Finish :mod:`sys` module initialization (ex: create :data:`sys.stdout`
2323+
and :data:`sys.path`);
2324+
* Enable optional features like :mod:`faulthandler` and :mod:`tracemalloc`;
2325+
* Import the :mod:`site` module;
2326+
* etc.
2327+
2328+
Private provisional API:
2329+
2330+
.. c:member:: int PyConfig._init_main
2331+
2332+
If set to ``0``, :c:func:`Py_InitializeFromConfig` stops at the "Core"
2333+
initialization phase.
2334+
2335+
.. c:function:: PyStatus _Py_InitializeMain(void)
2336+
2337+
Move to the "Main" initialization phase, finish the Python initialization.
2338+
2339+
No module is imported during the "Core" phase and the ``importlib`` module is
2340+
not configured: the :ref:`Path Configuration <init-path-config>` is only
2341+
applied during the "Main" phase. It may allow to customize Python in Python to
2342+
override or tune the :ref:`Path Configuration <init-path-config>`, maybe
2343+
install a custom :data:`sys.meta_path` importer or an import hook, etc.
2344+
2345+
It may become possible to calculate the :ref:`Path Configuration
2346+
<init-path-config>` in Python, after the Core phase and before the Main phase,
2347+
which is one of the :pep:`432` motivation.
2348+
2349+
The "Core" phase is not properly defined: what should be and what should
2350+
not be available at this phase is not specified yet. The API is marked
2351+
as private and provisional: the API can be modified or even be removed
2352+
anytime until a proper public API is designed.
2353+
2354+
Example running Python code between "Core" and "Main" initialization
2355+
phases::
2356+
2357+
void init_python(void)
2358+
{
2359+
PyStatus status;
2360+
2361+
PyConfig config;
2362+
PyConfig_InitPythonConfig(&config);
2363+
config._init_main = 0;
2364+
2365+
/* ... customize 'config' configuration ... */
2366+
2367+
status = Py_InitializeFromConfig(&config);
2368+
PyConfig_Clear(&config);
2369+
if (PyStatus_Exception(status)) {
2370+
Py_ExitStatusException(status);
2371+
}
2372+
2373+
/* Use sys.stderr because sys.stdout is only created
2374+
by _Py_InitializeMain() */
2375+
int res = PyRun_SimpleString(
2376+
"import sys; "
2377+
"print('Run Python code before _Py_InitializeMain', "
2378+
"file=sys.stderr)");
2379+
if (res < 0) {
2380+
exit(1);
2381+
}
2382+
2383+
/* ... put more configuration code here ... */
2384+
2385+
status = _Py_InitializeMain();
2386+
if (PyStatus_Exception(status)) {
2387+
Py_ExitStatusException(status);
2388+
}
2389+
}

Doc/whatsnew/3.15.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,10 @@ New features
16641664
* Add :c:func:`PyUnstable_SetImmortal` C-API function to mark objects as :term:`immortal`.
16651665
(Contributed by Kumar Aditya in :gh:`143300`.)
16661666

1667+
* Restore private provisional ``_Py_InitializeMain()`` function removed in
1668+
Python 3.14.
1669+
(Contributed by Victor Stinner in :gh:`142417`.)
1670+
16671671
Changed C APIs
16681672
--------------
16691673

Include/cpython/pylifecycle.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ PyAPI_FUNC(PyStatus) Py_PreInitializeFromArgs(
2525
PyAPI_FUNC(PyStatus) Py_InitializeFromConfig(
2626
const PyConfig *config);
2727

28+
// Python 3.8 provisional API (PEP 587)
29+
PyAPI_FUNC(PyStatus) _Py_InitializeMain(void);
30+
2831
PyAPI_FUNC(int) Py_RunMain(void);
2932

3033

Lib/test/test_embed.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,24 @@ def test_init_run_main(self):
13191319
}
13201320
self.check_all_configs("test_init_run_main", config, api=API_PYTHON)
13211321

1322+
def test_init_main(self):
1323+
code = ('import _testinternalcapi, json; '
1324+
'print(json.dumps(_testinternalcapi.get_configs()))')
1325+
config = {
1326+
'argv': ['-c', 'arg2'],
1327+
'orig_argv': ['python3',
1328+
'-c', code,
1329+
'arg2'],
1330+
'program_name': './python3',
1331+
'run_command': code + '\n',
1332+
'parse_argv': True,
1333+
'_init_main': False,
1334+
'sys_path_0': '',
1335+
}
1336+
self.check_all_configs("test_init_main", config,
1337+
api=API_PYTHON,
1338+
stderr="Run Python code before _Py_InitializeMain")
1339+
13221340
def test_init_parse_argv(self):
13231341
config = {
13241342
'parse_argv': True,

Lib/test/test_pyexpat.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,9 @@ def test_set_maximum_amplification__amplification_not_exceeded(self):
10691069
self.assertIsNotNone(parser.Parse(payload, True))
10701070

10711071

1072-
@unittest.skipIf(expat.version_info < (2, 7, 2), "requires Expat >= 2.7.2")
1072+
@unittest.skipIf(not hasattr(expat.XMLParserType,
1073+
"SetAllocTrackerMaximumAmplification"),
1074+
"requires Python compiled with Expat >= 2.7.2")
10731075
class MemoryProtectionTest(AttackProtectionTestBase, unittest.TestCase):
10741076

10751077
# NOTE: with the default Expat configuration, the billion laughs protection
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Restore private provisional ``_Py_InitializeMain()`` function removed in
2+
Python 3.14. Patch by Victor Stinner.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
When Python was compiled with system expat older then 2.7.2 but tests run
2+
with newer expat, still skip
3+
:class:`!test.test_pyexpat.MemoryProtectionTest`.

Modules/_functoolsmodule.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,11 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
252252
}
253253
PyObject *item;
254254
PyObject *tot_args = PyTuple_New(tot_nargs);
255+
if (tot_args == NULL) {
256+
Py_DECREF(new_args);
257+
Py_DECREF(pto);
258+
return NULL;
259+
}
255260
for (Py_ssize_t i = 0, j = 0; i < tot_nargs; i++) {
256261
if (i < npargs) {
257262
item = PyTuple_GET_ITEM(pto_args, i);

Modules/timemodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,8 @@ time_tzset(PyObject *self, PyObject *unused)
11701170

11711171
/* Reset timezone, altzone, daylight and tzname */
11721172
if (init_timezone(m) < 0) {
1173-
return NULL;
1173+
Py_DECREF(m);
1174+
return NULL;
11741175
}
11751176
Py_DECREF(m);
11761177
if (PyErr_Occurred())

Objects/exceptions.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
912912

913913
exceptions = PySequence_Tuple(exceptions);
914914
if (!exceptions) {
915-
return NULL;
915+
goto error;
916916
}
917917

918918
/* We are now holding a ref to the exceptions tuple */

0 commit comments

Comments
 (0)