Skip to content

Commit c7ec998

Browse files
bpo-22257: Private C-API for main interpreter initialization (PEP 432). (#1729)
(patch by Nick Coghlan)
1 parent 9e98cd0 commit c7ec998

File tree

5 files changed

+105
-18
lines changed

5 files changed

+105
-18
lines changed

Include/pylifecycle.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ PyAPI_FUNC(int) Py_SetStandardStreamEncoding(const char *encoding,
2323
/* PEP 432 Multi-phase initialization API (Private while provisional!) */
2424
PyAPI_FUNC(void) _Py_InitializeCore(const _PyCoreConfig *);
2525
PyAPI_FUNC(int) _Py_IsCoreInitialized(void);
26-
PyAPI_FUNC(int) _Py_InitializeMainInterpreter(int install_sigs);
26+
PyAPI_FUNC(int) _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *);
27+
PyAPI_FUNC(int) _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *);
2728
#endif
2829

2930
/* Initialization and finalization */

Include/pystate.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ typedef struct {
3333

3434
#define _PyCoreConfig_INIT {0, -1, 0, 0}
3535

36+
/* Placeholders while working on the new configuration API
37+
*
38+
* See PEP 432 for final anticipated contents
39+
*
40+
* For the moment, just handle the args to _Py_InitializeEx
41+
*/
42+
typedef struct {
43+
int install_signal_handlers;
44+
} _PyMainInterpreterConfig;
45+
46+
#define _PyMainInterpreterConfig_INIT {-1}
47+
3648
typedef struct _is {
3749

3850
struct _is *next;
@@ -53,6 +65,7 @@ typedef struct _is {
5365
int fscodec_initialized;
5466

5567
_PyCoreConfig core_config;
68+
_PyMainInterpreterConfig config;
5669
#ifdef HAVE_DLOPEN
5770
int dlopenflags;
5871
#endif

Modules/main.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,8 +768,21 @@ Py_Main(int argc, wchar_t **argv)
768768
#else
769769
Py_SetProgramName(argv[0]);
770770
#endif
771-
if (_Py_InitializeMainInterpreter(1))
772-
Py_FatalError("Py_Main: Py_InitializeMainInterpreter failed");
771+
/* Replaces previous call to Py_Initialize()
772+
*
773+
* TODO: Move environment queries (etc) into Py_ReadConfig
774+
*/
775+
{
776+
_PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT;
777+
778+
/* TODO: Moar config options! */
779+
config.install_signal_handlers = 1;
780+
/* TODO: Print any exceptions raised by these operations */
781+
if (_Py_ReadMainInterpreterConfig(&config))
782+
Py_FatalError("Py_Main: Py_ReadMainInterpreterConfig failed");
783+
if (_Py_InitializeMainInterpreter(&config))
784+
Py_FatalError("Py_Main: Py_InitializeMainInterpreter failed");
785+
}
773786

774787
/* TODO: Move this to _PyRun_PrepareMain */
775788
if (!Py_QuietFlag && (Py_VerboseFlag ||

Python/pylifecycle.c

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ extern void _PyFaulthandler_Fini(void);
6969
extern void _PyHash_Fini(void);
7070
extern int _PyTraceMalloc_Init(void);
7171
extern int _PyTraceMalloc_Fini(void);
72+
extern void _Py_ReadyTypes(void);
7273

7374
#ifdef WITH_THREAD
7475
extern void _PyGILState_Init(PyInterpreterState *, PyThreadState *);
@@ -112,6 +113,7 @@ PyModule_GetWarningsModule(void)
112113
return PyImport_ImportModule("warnings");
113114
}
114115

116+
115117
/* APIs to access the initialization flags
116118
*
117119
* Can be called prior to Py_Initialize.
@@ -366,8 +368,8 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
366368
PyThreadState *tstate;
367369
PyObject *bimod, *sysmod, *pstderr;
368370
char *p;
369-
extern void _Py_ReadyTypes(void);
370371
_PyCoreConfig core_config = _PyCoreConfig_INIT;
372+
_PyMainInterpreterConfig preinit_config = _PyMainInterpreterConfig_INIT;
371373

372374
if (config != NULL) {
373375
core_config = *config;
@@ -428,6 +430,7 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
428430
if (interp == NULL)
429431
Py_FatalError("Py_InitializeCore: can't make main interpreter");
430432
interp->core_config = core_config;
433+
interp->config = preinit_config;
431434

432435
tstate = PyThreadState_New(interp);
433436
if (tstate == NULL)
@@ -518,21 +521,62 @@ void _Py_InitializeCore(const _PyCoreConfig *config)
518521
_Py_CoreInitialized = 1;
519522
}
520523

521-
int
522-
_Py_InitializeMainInterpreter(int install_sigs)
524+
/* Read configuration settings from standard locations
525+
*
526+
* This function doesn't make any changes to the interpreter state - it
527+
* merely populates any missing configuration settings. This allows an
528+
* embedding application to completely override a config option by
529+
* setting it before calling this function, or else modify the default
530+
* setting before passing the fully populated config to Py_EndInitialization.
531+
*
532+
* More advanced selective initialization tricks are possible by calling
533+
* this function multiple times with various preconfigured settings.
534+
*/
535+
536+
int _Py_ReadMainInterpreterConfig(_PyMainInterpreterConfig *config)
537+
{
538+
/* Signal handlers are installed by default */
539+
if (config->install_signal_handlers < 0) {
540+
config->install_signal_handlers = 1;
541+
}
542+
543+
return 0;
544+
}
545+
546+
/* Update interpreter state based on supplied configuration settings
547+
*
548+
* After calling this function, most of the restrictions on the interpreter
549+
* are lifted. The only remaining incomplete settings are those related
550+
* to the main module (sys.argv[0], __main__ metadata)
551+
*
552+
* Calling this when the interpreter is not initializing, is already
553+
* initialized or without a valid current thread state is a fatal error.
554+
* Other errors should be reported as normal Python exceptions with a
555+
* non-zero return code.
556+
*/
557+
int _Py_InitializeMainInterpreter(const _PyMainInterpreterConfig *config)
523558
{
524559
PyInterpreterState *interp;
525560
PyThreadState *tstate;
526561

562+
if (!_Py_CoreInitialized) {
563+
Py_FatalError("Py_InitializeMainInterpreter: runtime core not initialized");
564+
}
565+
if (_Py_Initialized) {
566+
Py_FatalError("Py_InitializeMainInterpreter: main interpreter already initialized");
567+
}
568+
527569
/* Get current thread state and interpreter pointer */
528570
tstate = PyThreadState_GET();
529571
if (!tstate)
530-
Py_FatalError("Py_Initialize: failed to read thread state");
572+
Py_FatalError("Py_InitializeMainInterpreter: failed to read thread state");
531573
interp = tstate->interp;
532574
if (!interp)
533-
Py_FatalError("Py_Initialize: failed to get interpreter");
575+
Py_FatalError("Py_InitializeMainInterpreter: failed to get interpreter");
534576

535577
/* Now finish configuring the main interpreter */
578+
interp->config = *config;
579+
536580
if (interp->core_config._disable_importlib) {
537581
/* Special mode for freeze_importlib: run with no import system
538582
*
@@ -545,7 +589,7 @@ _Py_InitializeMainInterpreter(int install_sigs)
545589
/* TODO: Report exceptions rather than fatal errors below here */
546590

547591
if (_PyTime_Init() < 0)
548-
Py_FatalError("Py_Initialize: can't initialize time");
592+
Py_FatalError("Py_InitializeMainInterpreter: can't initialize time");
549593

550594
/* Finish setting up the sys module and import system */
551595
/* GetPath may initialize state that _PySys_EndInit locks
@@ -559,21 +603,21 @@ _Py_InitializeMainInterpreter(int install_sigs)
559603

560604
/* initialize the faulthandler module */
561605
if (_PyFaulthandler_Init())
562-
Py_FatalError("Py_Initialize: can't initialize faulthandler");
606+
Py_FatalError("Py_InitializeMainInterpreter: can't initialize faulthandler");
563607

564608
if (initfsencoding(interp) < 0)
565-
Py_FatalError("Py_Initialize: unable to load the file system codec");
609+
Py_FatalError("Py_InitializeMainInterpreter: unable to load the file system codec");
566610

567-
if (install_sigs)
611+
if (config->install_signal_handlers)
568612
initsigs(); /* Signal handling stuff, including initintr() */
569613

570614
if (_PyTraceMalloc_Init() < 0)
571-
Py_FatalError("Py_Initialize: can't initialize tracemalloc");
615+
Py_FatalError("Py_InitializeMainInterpreter: can't initialize tracemalloc");
572616

573617
initmain(interp); /* Module __main__ */
574618
if (initstdio() < 0)
575619
Py_FatalError(
576-
"Py_Initialize: can't initialize sys standard streams");
620+
"Py_InitializeMainInterpreter: can't initialize sys standard streams");
577621

578622
/* Initialize warnings. */
579623
if (PySys_HasWarnOptions()) {
@@ -590,19 +634,27 @@ _Py_InitializeMainInterpreter(int install_sigs)
590634
if (!Py_NoSiteFlag)
591635
initsite(); /* Module site */
592636

593-
return 0;
637+
return 0;
594638
}
595639

640+
#undef _INIT_DEBUG_PRINT
641+
596642
void
597643
_Py_InitializeEx_Private(int install_sigs, int install_importlib)
598644
{
599645
_PyCoreConfig core_config = _PyCoreConfig_INIT;
646+
_PyMainInterpreterConfig config = _PyMainInterpreterConfig_INIT;
600647

601648
/* TODO: Moar config options! */
602649
core_config.ignore_environment = Py_IgnoreEnvironmentFlag;
603650
core_config._disable_importlib = !install_importlib;
651+
config.install_signal_handlers = install_sigs;
604652
_Py_InitializeCore(&core_config);
605-
_Py_InitializeMainInterpreter(install_sigs);
653+
/* TODO: Print any exceptions raised by these operations */
654+
if (_Py_ReadMainInterpreterConfig(&config))
655+
Py_FatalError("Py_Initialize: Py_ReadMainInterpreterConfig failed");
656+
if (_Py_InitializeMainInterpreter(&config))
657+
Py_FatalError("Py_Initialize: Py_InitializeMainInterpreter failed");
606658
}
607659

608660

@@ -932,10 +984,12 @@ Py_NewInterpreter(void)
932984
/* Copy the current interpreter config into the new interpreter */
933985
if (save_tstate != NULL) {
934986
interp->core_config = save_tstate->interp->core_config;
987+
interp->config = save_tstate->interp->config;
935988
} else {
936989
/* No current thread state, copy from the main interpreter */
937990
PyInterpreterState *main_interp = PyInterpreterState_Main();
938991
interp->core_config = main_interp->core_config;
992+
interp->config = main_interp->config;
939993
}
940994

941995
/* XXX The following is lax in error checking */

Python/pythonrun.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11

2-
/* Python interpreter top-level routines, including init/exit */
2+
/* Top level execution of Python code (including in __main__) */
3+
4+
/* To help control the interfaces between the startup, execution and
5+
* shutdown code, the phases are split across separate modules (boostrap,
6+
* pythonrun, shutdown)
7+
*/
8+
9+
/* TODO: Cull includes following phase split */
310

411
#include "Python.h"
512

@@ -59,7 +66,6 @@ static void err_input(perrdetail *);
5966
static void err_free(perrdetail *);
6067

6168
/* Parse input from a file and execute it */
62-
6369
int
6470
PyRun_AnyFileExFlags(FILE *fp, const char *filename, int closeit,
6571
PyCompilerFlags *flags)

0 commit comments

Comments
 (0)