From 3daa65ac8d24252965bb09b497993e4587b01f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 12 Oct 2023 16:34:37 +0200 Subject: [PATCH 1/9] gh-110722: Add PYTHON_PRESITE=package.module to import a module before site.py is run This is only available --with-pydebug. --- Doc/c-api/init_config.rst | 15 +++++++++++ Include/cpython/initconfig.h | 6 +++++ Lib/test/test_embed.py | 2 ++ Python/initconfig.c | 51 ++++++++++++++++++++++++++++++++++++ Python/pylifecycle.c | 21 +++++++++++++++ 5 files changed, 95 insertions(+) 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; From 1ef282b75e78dce1cc035a24746a49c5c2eaae04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 12 Oct 2023 17:15:28 +0200 Subject: [PATCH 2/9] Add Blurb and fix a refleak before anybody notices --- Doc/using/cmdline.rst | 16 ++++++++++++++++ ...023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst | 3 +++ Python/initconfig.c | 3 +++ Python/pylifecycle.c | 3 ++- 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 2767b0cb15451c..6ba46de3195713 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -552,6 +552,10 @@ Miscellaneous options This option may be useful for users who need to limit CPU resources of a container system. See also :envvar:`PYTHON_CPU_COUNT`. If *n* is ``default``, nothing is overridden. + * :samp:`-X presite={package.module}` specifies a module that should be + imported before ``site.py`` is executed. Python needs to be + :ref:`built in debug mode ` for this option to exist. + See also :envvar:`PYTHON_PRESITE `. It also allows passing arbitrary values and retrieving them through the :data:`sys._xoptions` dictionary. @@ -602,6 +606,8 @@ Miscellaneous options .. versionadded:: 3.13 The ``-X cpu_count`` option. + The ``-X presite`` option. + Options you shouldn't use ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1100,3 +1106,13 @@ Debug-mode variables Need Python configured with the :option:`--with-trace-refs` build option. .. versionadded:: 3.11 + +.. envvar:: PYTHON_PRESITE=package.module + + If this variable is set to a module, that module will be imported + early in the interpreter lifecycle, before ``site.py`` is executed. + + See also the :option:`-X presite <-X>` command-line option, + which takes precedence over this variable. + + .. versionadded:: 3.13 diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst new file mode 100644 index 00000000000000..79b941ef6879f8 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-12-17-15-23.gh-issue-110722.sjMwQe.rst @@ -0,0 +1,3 @@ +Add :envvar:`PYTHON_PRESITE=package.module` to import a module early in the +interpreter lifecycle before ``site.py`` is executed. Python needs to be +:ref:`built in debug mode ` for this option to exist. diff --git a/Python/initconfig.c b/Python/initconfig.c index 562d71f568a379..5389196636cb48 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -305,6 +305,9 @@ static const char usage_envvars[] = #ifdef Py_STATS "PYTHONSTATS : turns on statistics gathering\n" #endif +#ifdef Py_DEBUG +"PYTHON_PRESITE=pkg.mod : import this module before site.py is run\n" +#endif ; #if defined(MS_WINDOWS) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 16e24302130f96..771beefec27b6a 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1170,10 +1170,11 @@ init_interp_main(PyThreadState *tstate) else { PyObject *presite = PyImport_Import(presite_modname); if (presite == NULL) { - fprintf(stderr, "pre-site import failed; traceback:\n"); + fprintf(stderr, "pre-site import failed:\n"); _PyErr_Print(tstate); } Py_XDECREF(presite); + Py_DECREF(presite_modname); } } #endif From 8a201b4a63069982d9670d3bc595262f90a20e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 12 Oct 2023 17:18:15 +0200 Subject: [PATCH 3/9] Fix versionadded --- Doc/using/cmdline.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 6ba46de3195713..8d50e8f81c0e65 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -606,6 +606,7 @@ Miscellaneous options .. versionadded:: 3.13 The ``-X cpu_count`` option. + .. versionadded:: 3.13 The ``-X presite`` option. From b89548d9e5f55331dde9c0a9a844a235d734373f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 13 Oct 2023 10:36:48 +0200 Subject: [PATCH 4/9] Add test, refine docs, add whatsnew, separate run_presite in pylifecycle.c --- Doc/using/cmdline.rst | 18 ++++++++++------ Doc/whatsnew/3.13.rst | 10 +++++++++ Lib/test/test_embed.py | 16 ++++++++++++++ Python/pylifecycle.c | 48 +++++++++++++++++++++++++----------------- 4 files changed, 66 insertions(+), 26 deletions(-) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 8d50e8f81c0e65..9977ab1bbeb871 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -553,9 +553,10 @@ Miscellaneous options container system. See also :envvar:`PYTHON_CPU_COUNT`. If *n* is ``default``, nothing is overridden. * :samp:`-X presite={package.module}` specifies a module that should be - imported before ``site.py`` is executed. Python needs to be - :ref:`built in debug mode ` for this option to exist. - See also :envvar:`PYTHON_PRESITE `. + imported before ``site.py`` is executed and before the :mod:`__main__` + module exists. Therefore, the imported module isn't :mod:`__main__`. + Python needs to be :ref:`built in debug mode ` for this + option to exist. See also :envvar:`PYTHON_PRESITE`. It also allows passing arbitrary values and retrieving them through the :data:`sys._xoptions` dictionary. @@ -1099,19 +1100,22 @@ Debug-mode variables Need Python configured with the :option:`--with-trace-refs` build option. -.. envvar:: PYTHONDUMPREFSFILE=FILENAME +.. envvar:: PYTHONDUMPREFSFILE If set, Python will dump objects and reference counts still alive - after shutting down the interpreter into a file called *FILENAME*. + after shutting down the interpreter into a file under the path given + as the value to this environment variable. Need Python configured with the :option:`--with-trace-refs` build option. .. versionadded:: 3.11 -.. envvar:: PYTHON_PRESITE=package.module +.. envvar:: PYTHON_PRESITE If this variable is set to a module, that module will be imported - early in the interpreter lifecycle, before ``site.py`` is executed. + early in the interpreter lifecycle, before ``site.py`` is executed, + and before the :mod:`__main__` module is created. Therefore, the + imported module is not treated as :mod:`__main__`. See also the :option:`-X presite <-X>` command-line option, which takes precedence over this variable. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index dfce976fbb50ee..9b0b6f43cbed0a 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -1348,3 +1348,13 @@ removed, although there is currently no date scheduled for their removal. * Remove undocumented ``PY_TIMEOUT_MAX`` constant from the limited C API. (Contributed by Victor Stinner in :gh:`110014`.) + + +Regression Test Changes +======================= + +* Python built with :file:`configure` :option:`--with-pydebug` now + supports a :option:`-X presite=package.module <-X>` command-line + option. If used, it specifies a module that should be imported early + in the lifecycle of the interpreter, before ``site.py`` is executed. + (Contributed by Ɓukasz Langa in :gh:`110769`.) diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index cfb4ba4aa26000..d2d6c1b61e46f0 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -1820,6 +1820,22 @@ def test_no_memleak(self): self.assertEqual(refs, 0, out) self.assertEqual(blocks, 0, out) + @unittest.skipUnless(support.Py_DEBUG, + '-X presite requires a Python debug build') + def test_presite(self): + cmd = [sys.executable, "-I", "-X", "presite=test.reperf", "-c", "print('cmd')"] + proc = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) + self.assertEqual(proc.returncode, 0) + out = proc.stdout.strip() + self.assertIn("10 times sub", out) + self.assertIn("CPU seconds", out) + self.assertIn("cmd", out) + class StdPrinterTests(EmbeddingTestsMixin, unittest.TestCase): # Test PyStdPrinter_Type which is used by _PySys_SetPreliminaryStderr(): diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 771beefec27b6a..72148b56435e5a 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1076,6 +1076,34 @@ pyinit_main_reconfigure(PyThreadState *tstate) } +static void +run_presite(PyThreadState *tstate) +{ + PyInterpreterState *interp = tstate->interp; + const PyConfig *config = _PyInterpreterState_GetConfig(interp); + + 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 pre-site 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:\n"); + _PyErr_Print(tstate); + } + Py_XDECREF(presite); + Py_DECREF(presite_modname); + } +} +} + + static PyStatus init_interp_main(PyThreadState *tstate) { @@ -1158,25 +1186,7 @@ init_interp_main(PyThreadState *tstate) } #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:\n"); - _PyErr_Print(tstate); - } - Py_XDECREF(presite); - Py_DECREF(presite_modname); - } - } + run_presite(tstate); #endif status = add_main_module(interp); From ebcc37335046e7be519da9a1707ca718e6e4a53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 13 Oct 2023 10:42:24 +0200 Subject: [PATCH 5/9] PEP 7 --- Python/initconfig.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Python/initconfig.c b/Python/initconfig.c index 5389196636cb48..e1199338f2a54f 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -1907,7 +1907,7 @@ config_read_complex_options(PyConfig *config) } #ifdef Py_DEBUG - if (config->run_presite== NULL) { + if (config->run_presite == NULL) { status = config_init_run_presite(config); if (_PyStatus_EXCEPTION(status)) { return status; From bebb594697228f2405999c670e66ad825ae440bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 13 Oct 2023 10:45:44 +0200 Subject: [PATCH 6/9] Only define the static function --with-pydebug --- Python/pylifecycle.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 72148b56435e5a..1931ecd0a64d23 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1076,6 +1076,7 @@ pyinit_main_reconfigure(PyThreadState *tstate) } +#ifdef Py_DEBUG static void run_presite(PyThreadState *tstate) { @@ -1102,6 +1103,7 @@ run_presite(PyThreadState *tstate) } } } +#endif static PyStatus From 08679d85395cd63ab2ce32ccf7fd000f164f1f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 13 Oct 2023 17:03:39 +0200 Subject: [PATCH 7/9] Fix visual indentation --- Python/pylifecycle.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 1931ecd0a64d23..7b56034541756a 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1083,8 +1083,11 @@ run_presite(PyThreadState *tstate) PyInterpreterState *interp = tstate->interp; const PyConfig *config = _PyInterpreterState_GetConfig(interp); - if (config->run_presite) { - PyObject *presite_modname = PyUnicode_FromWideChar( + if (!config->run_presite) { + return; + } + + PyObject *presite_modname = PyUnicode_FromWideChar( config->run_presite, wcslen(config->run_presite) ); @@ -1102,7 +1105,6 @@ run_presite(PyThreadState *tstate) Py_DECREF(presite_modname); } } -} #endif From 3d3f5d36902a296d4bc232a87301bf6babfd2d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 13 Oct 2023 17:43:36 +0200 Subject: [PATCH 8/9] Mention PYTHON_PRESITE needs a pydebug build --- Doc/c-api/init_config.rst | 8 ++++---- Doc/using/cmdline.rst | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 8ca26d9d43a2e9..1d4e0fbb0d400f 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -716,7 +716,7 @@ PyConfig Set to ``1`` by the :envvar:`PYTHONDUMPREFS` environment variable. - Need a special build of Python with the ``Py_TRACE_REFS`` macro defined: + Needs a special build of Python with the ``Py_TRACE_REFS`` macro defined: see the :option:`configure --with-trace-refs option <--with-trace-refs>`. Default: ``0``. @@ -1048,7 +1048,7 @@ PyConfig Incremented by the :option:`-d` command line option. Set to the :envvar:`PYTHONDEBUG` environment variable value. - Need a :ref:`debug build of Python ` (the ``Py_DEBUG`` macro + Needs a :ref:`debug build of Python ` (the ``Py_DEBUG`` macro must be defined). Default: ``0``. @@ -1153,7 +1153,7 @@ PyConfig 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 + Needs a :ref:`debug build of Python ` (the ``Py_DEBUG`` macro must be defined). Default: ``NULL``. @@ -1164,7 +1164,7 @@ PyConfig Set to ``1`` by :option:`-X showrefcount <-X>` command line option. - Need a :ref:`debug build of Python ` (the ``Py_REF_DEBUG`` + Needs a :ref:`debug build of Python ` (the ``Py_REF_DEBUG`` macro must be defined). Default: ``0``. diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 9977ab1bbeb871..09d5ed817c96c8 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -1098,7 +1098,7 @@ Debug-mode variables If set, Python will dump objects and reference counts still alive after shutting down the interpreter. - Need Python configured with the :option:`--with-trace-refs` build option. + Needs Python configured with the :option:`--with-trace-refs` build option. .. envvar:: PYTHONDUMPREFSFILE @@ -1106,7 +1106,7 @@ Debug-mode variables after shutting down the interpreter into a file under the path given as the value to this environment variable. - Need Python configured with the :option:`--with-trace-refs` build option. + Needs Python configured with the :option:`--with-trace-refs` build option. .. versionadded:: 3.11 @@ -1120,4 +1120,6 @@ Debug-mode variables See also the :option:`-X presite <-X>` command-line option, which takes precedence over this variable. + Needs Python configured with the :option:`--with-pydebug` build option. + .. versionadded:: 3.13 From 2e1d1805b5302ad3529d43b08271ae5de8649be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Sat, 14 Oct 2023 18:07:10 +0200 Subject: [PATCH 9/9] Address Victor's nits --- Doc/using/cmdline.rst | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst index 09d5ed817c96c8..4a9b2c65c39a18 100644 --- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -553,10 +553,11 @@ Miscellaneous options container system. See also :envvar:`PYTHON_CPU_COUNT`. If *n* is ``default``, nothing is overridden. * :samp:`-X presite={package.module}` specifies a module that should be - imported before ``site.py`` is executed and before the :mod:`__main__` - module exists. Therefore, the imported module isn't :mod:`__main__`. - Python needs to be :ref:`built in debug mode ` for this - option to exist. See also :envvar:`PYTHON_PRESITE`. + imported before the :mod:`site` module is executed and before the + :mod:`__main__` module exists. Therefore, the imported module isn't + :mod:`__main__`. This can be used to execute code early during Python + initialization. Python needs to be :ref:`built in debug mode ` + for this option to exist. See also :envvar:`PYTHON_PRESITE`. It also allows passing arbitrary values and retrieving them through the :data:`sys._xoptions` dictionary. @@ -1113,9 +1114,14 @@ Debug-mode variables .. envvar:: PYTHON_PRESITE If this variable is set to a module, that module will be imported - early in the interpreter lifecycle, before ``site.py`` is executed, - and before the :mod:`__main__` module is created. Therefore, the - imported module is not treated as :mod:`__main__`. + early in the interpreter lifecycle, before the :mod:`site` module is + executed, and before the :mod:`__main__` module is created. + Therefore, the imported module is not treated as :mod:`__main__`. + + This can be used to execute code early during Python initialization. + + To import a submodule, use ``package.module`` as the value, like in + an import statement. See also the :option:`-X presite <-X>` command-line option, which takes precedence over this variable.