diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 0240e25b6f1607..8ca26d9d43a2e9 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -1100,6 +1100,7 @@ PyConfig Set by the :option:`-X pycache_prefix=PATH <-X>` command line option and the :envvar:`PYTHONPYCACHEPREFIX` environment variable. + The command-line option takes precedence. If ``NULL``, :data:`sys.pycache_prefix` is set to ``None``. @@ -1143,6 +1144,20 @@ PyConfig Default: ``NULL``. + .. c:member:: wchar_t* run_presite + + ``package.module`` path to module that should be imported before + ``site.py`` is run. + + Set by the :option:`-X presite=package.module <-X>` command-line + option and the :envvar:`PYTHON_PRESITE` environment variable. + The command-line option takes precedence. + + Need a :ref:`debug build of Python ` (the ``Py_DEBUG`` macro + must be defined). + + Default: ``NULL``. + .. c:member:: int show_ref_count Show total reference count at exit (excluding immortal objects)? diff --git a/Include/cpython/initconfig.h b/Include/cpython/initconfig.h index 808c1056498b49..87c059c521cbc9 100644 --- a/Include/cpython/initconfig.h +++ b/Include/cpython/initconfig.h @@ -225,6 +225,12 @@ typedef struct PyConfig { // If non-zero, turns on statistics gathering. int _pystats; #endif + +#ifdef Py_DEBUG + // If not empty, import a non-__main__ module before site.py is executed. + // PYTHON_PRESITE=package.module or -X presite=package.module + wchar_t *run_presite; +#endif } PyConfig; PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config); diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 5a8690a4836dd6..cfb4ba4aa26000 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -515,6 +515,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): } if Py_STATS: CONFIG_COMPAT['_pystats'] = 0 + if support.Py_DEBUG: + CONFIG_COMPAT['run_presite'] = None if MS_WINDOWS: CONFIG_COMPAT.update({ 'legacy_windows_stdio': 0, diff --git a/Python/initconfig.c b/Python/initconfig.c index f7eb8535e98a6a..562d71f568a379 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -117,6 +117,9 @@ static const PyConfigSpec PYCONFIG_SPEC[] = { SPEC(_is_python_build, UINT), #ifdef Py_STATS SPEC(_pystats, UINT), +#endif +#ifdef Py_DEBUG + SPEC(run_presite, WSTR_OPT), #endif {NULL, 0, 0}, }; @@ -241,6 +244,11 @@ The following implementation-specific options are available:\n\ \n\ -X pystats: Enable pystats collection at startup." #endif +#ifdef Py_DEBUG +"\n\ +\n\ +-X presite=package.module: import this module before site.py is run." +#endif ; /* Envvars that don't have equivalent command-line options are listed first */ @@ -790,6 +798,9 @@ PyConfig_Clear(PyConfig *config) CLEAR(config->run_module); CLEAR(config->run_filename); CLEAR(config->check_hash_pycs_mode); +#ifdef Py_DEBUG + CLEAR(config->run_presite); +#endif _PyWideStringList_Clear(&config->orig_argv); #undef CLEAR @@ -1806,6 +1817,36 @@ config_init_pycache_prefix(PyConfig *config) } +#ifdef Py_DEBUG +static PyStatus +config_init_run_presite(PyConfig *config) +{ + assert(config->run_presite == NULL); + + const wchar_t *xoption = config_get_xoption(config, L"presite"); + if (xoption) { + const wchar_t *sep = wcschr(xoption, L'='); + if (sep && wcslen(sep) > 1) { + config->run_presite = _PyMem_RawWcsdup(sep + 1); + if (config->run_presite == NULL) { + return _PyStatus_NO_MEMORY(); + } + } + else { + // PYTHON_PRESITE env var ignored + // if "-X presite=" option is used + config->run_presite = NULL; + } + return _PyStatus_OK(); + } + + return CONFIG_GET_ENV_DUP(config, &config->run_presite, + L"PYTHON_PRESITE", + "PYTHON_PRESITE"); +} +#endif + + static PyStatus config_read_complex_options(PyConfig *config) { @@ -1861,6 +1902,16 @@ config_read_complex_options(PyConfig *config) return status; } } + +#ifdef Py_DEBUG + if (config->run_presite== NULL) { + status = config_init_run_presite(config); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + } +#endif + return _PyStatus_OK(); } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 14033162377489..16e24302130f96 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1157,6 +1157,27 @@ init_interp_main(PyThreadState *tstate) return status; } +#ifdef Py_DEBUG + if (config->run_presite) { + PyObject *presite_modname = PyUnicode_FromWideChar( + config->run_presite, + wcslen(config->run_presite) + ); + if (presite_modname == NULL) { + fprintf(stderr, "Could not convert module name to unicode\n"); + Py_DECREF(presite_modname); + } + else { + PyObject *presite = PyImport_Import(presite_modname); + if (presite == NULL) { + fprintf(stderr, "pre-site import failed; traceback:\n"); + _PyErr_Print(tstate); + } + Py_XDECREF(presite); + } + } +#endif + status = add_main_module(interp); if (_PyStatus_EXCEPTION(status)) { return status;