Skip to content

Commit

Permalink
pythongh-110722: Add PYTHON_PRESITE=package.module to import a module…
Browse files Browse the repository at this point in the history
… before site.py is run

This is only available --with-pydebug.
  • Loading branch information
ambv committed Oct 12, 2023
1 parent eb50cd3 commit 3daa65a
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 0 deletions.
15 changes: 15 additions & 0 deletions Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
Expand Down Expand Up @@ -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 <debug-build>` (the ``Py_DEBUG`` macro
must be defined).
Default: ``NULL``.
.. c:member:: int show_ref_count
Show total reference count at exit (excluding immortal objects)?
Expand Down
6 changes: 6 additions & 0 deletions Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
51 changes: 51 additions & 0 deletions Python/initconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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();
}

Expand Down
21 changes: 21 additions & 0 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 3daa65a

Please sign in to comment.