bpo-42260: Fix _PyConfig_Read() if compute_path_config=0 (GH-23220)

Fix _PyConfig_Read() if compute_path_config=0: use values set by
Py_SetPath(), Py_SetPythonHome() and Py_SetProgramName(). Add
compute_path_config parameter to _PyConfig_InitPathConfig().

The following functions now return NULL if called before
Py_Initialize():

* Py_GetExecPrefix()
* Py_GetPath()
* Py_GetPrefix()
* Py_GetProgramFullPath()
* Py_GetProgramName()
* Py_GetPythonHome()

These functions no longer automatically computes the Python Path
Configuration. Moreover, Py_SetPath() no longer computes
program_full_path.
This commit is contained in:
Victor Stinner 2020-11-10 21:10:22 +01:00 committed by GitHub
parent 1e996c3a3b
commit ace3f9a0ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 93 additions and 95 deletions

View File

@ -376,6 +376,12 @@ Process-wide parameters
The returned string points into static storage; the caller should not modify its The returned string points into static storage; the caller should not modify its
value. value.
This function should not be called before :c:func:`Py_Initialize`, otherwise
it returns ``NULL``.
.. versionchanged:: 3.10
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
.. c:function:: wchar_t* Py_GetPrefix() .. c:function:: wchar_t* Py_GetPrefix()
@ -389,6 +395,12 @@ Process-wide parameters
script at build time. The value is available to Python code as ``sys.prefix``. script at build time. The value is available to Python code as ``sys.prefix``.
It is only useful on Unix. See also the next function. It is only useful on Unix. See also the next function.
This function should not be called before :c:func:`Py_Initialize`, otherwise
it returns ``NULL``.
.. versionchanged:: 3.10
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
.. c:function:: wchar_t* Py_GetExecPrefix() .. c:function:: wchar_t* Py_GetExecPrefix()
@ -424,6 +436,12 @@ Process-wide parameters
while having :file:`/usr/local/plat` be a different filesystem for each while having :file:`/usr/local/plat` be a different filesystem for each
platform. platform.
This function should not be called before :c:func:`Py_Initialize`, otherwise
it returns ``NULL``.
.. versionchanged:: 3.10
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
.. c:function:: wchar_t* Py_GetProgramFullPath() .. c:function:: wchar_t* Py_GetProgramFullPath()
@ -437,6 +455,12 @@ Process-wide parameters
static storage; the caller should not modify its value. The value is available static storage; the caller should not modify its value. The value is available
to Python code as ``sys.executable``. to Python code as ``sys.executable``.
This function should not be called before :c:func:`Py_Initialize`, otherwise
it returns ``NULL``.
.. versionchanged:: 3.10
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
.. c:function:: wchar_t* Py_GetPath() .. c:function:: wchar_t* Py_GetPath()
@ -455,8 +479,14 @@ Process-wide parameters
can be (and usually is) modified later to change the search path for loading can be (and usually is) modified later to change the search path for loading
modules. modules.
This function should not be called before :c:func:`Py_Initialize`, otherwise
it returns ``NULL``.
.. XXX should give the exact rules .. XXX should give the exact rules
.. versionchanged:: 3.10
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
.. c:function:: void Py_SetPath(const wchar_t *) .. c:function:: void Py_SetPath(const wchar_t *)
@ -638,6 +668,12 @@ Process-wide parameters
:c:func:`Py_SetPythonHome`, or the value of the :envvar:`PYTHONHOME` :c:func:`Py_SetPythonHome`, or the value of the :envvar:`PYTHONHOME`
environment variable if it is set. environment variable if it is set.
This function should not be called before :c:func:`Py_Initialize`, otherwise
it returns ``NULL``.
.. versionchanged:: 3.10
It now returns ``NULL`` if called before :c:func:`Py_Initialize`.
.. _threads: .. _threads:

View File

@ -24,11 +24,15 @@ There are two kinds of configuration:
environments variables are ignored, the LC_CTYPE locale is left unchanged and environments variables are ignored, the LC_CTYPE locale is left unchanged and
no signal handler is registred. no signal handler is registred.
The :c:func:`Py_RunMain` function can be used to write a customized Python
program.
See also :ref:`Initialization, Finalization, and Threads <initialization>`. See also :ref:`Initialization, Finalization, and Threads <initialization>`.
.. seealso:: .. seealso::
:pep:`587` "Python Initialization Configuration". :pep:`587` "Python Initialization Configuration".
Example Example
======= =======
@ -532,7 +536,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: wchar_t* base_executable .. c:member:: wchar_t* base_executable
@ -544,7 +548,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: wchar_t* base_prefix .. c:member:: wchar_t* base_prefix
@ -552,7 +556,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: int buffered_stdio .. c:member:: int buffered_stdio
@ -634,7 +638,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: wchar_t* executable .. c:member:: wchar_t* executable
@ -643,7 +647,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: int faulthandler .. c:member:: int faulthandler
@ -726,7 +730,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` input. Part of the :ref:`Python Path Configuration <init-path-config>` input.
.. c:member:: int import_time .. c:member:: int import_time
@ -817,7 +821,7 @@ PyConfig
Default: value of the ``PLATLIBDIR`` macro which is set at configure time Default: value of the ``PLATLIBDIR`` macro which is set at configure time
by ``--with-platlibdir`` (default: ``"lib"``). by ``--with-platlibdir`` (default: ``"lib"``).
Part of the :ref:`Path Configuration <init-path-config>` input. Part of the :ref:`Python Path Configuration <init-path-config>` input.
.. versionadded:: 3.9 .. versionadded:: 3.9
@ -830,7 +834,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` input. Part of the :ref:`Python Path Configuration <init-path-config>` input.
.. c:member:: PyWideStringList module_search_paths .. c:member:: PyWideStringList module_search_paths
.. c:member:: int module_search_paths_set .. c:member:: int module_search_paths_set
@ -838,14 +842,14 @@ PyConfig
Module search paths: :data:`sys.path`. Module search paths: :data:`sys.path`.
If :c:member:`~PyConfig.module_search_paths_set` is equal to 0, the If :c:member:`~PyConfig.module_search_paths_set` is equal to 0, the
function calculating the :ref:`Path Configuration <init-path-config>` function calculating the :ref:`Python Path Configuration <init-path-config>`
overrides the :c:member:`~PyConfig.module_search_paths` and sets overrides the :c:member:`~PyConfig.module_search_paths` and sets
:c:member:`~PyConfig.module_search_paths_set` to ``1``. :c:member:`~PyConfig.module_search_paths_set` to ``1``.
Default: empty list (``module_search_paths``) and ``0`` Default: empty list (``module_search_paths``) and ``0``
(``module_search_paths_set``). (``module_search_paths_set``).
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: int optimization_level .. c:member:: int optimization_level
@ -911,7 +915,7 @@ PyConfig
.. c:member:: int pathconfig_warnings .. c:member:: int pathconfig_warnings
On Unix, if non-zero, calculating the :ref:`Path Configuration On Unix, if non-zero, calculating the :ref:`Python Path Configuration
<init-path-config>` can log warnings into ``stderr``. If equals to 0, <init-path-config>` can log warnings into ``stderr``. If equals to 0,
suppress these warnings. suppress these warnings.
@ -919,7 +923,7 @@ PyConfig
Default: ``1`` in Python mode, ``0`` in isolated mode. Default: ``1`` in Python mode, ``0`` in isolated mode.
Part of the :ref:`Path Configuration <init-path-config>` input. Part of the :ref:`Python Path Configuration <init-path-config>` input.
.. c:member:: wchar_t* prefix .. c:member:: wchar_t* prefix
@ -928,7 +932,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` output. Part of the :ref:`Python Path Configuration <init-path-config>` output.
.. c:member:: wchar_t* program_name .. c:member:: wchar_t* program_name
@ -946,7 +950,7 @@ PyConfig
Default: ``NULL``. Default: ``NULL``.
Part of the :ref:`Path Configuration <init-path-config>` input. Part of the :ref:`Python Path Configuration <init-path-config>` input.
.. c:member:: wchar_t* pycache_prefix .. c:member:: wchar_t* pycache_prefix
@ -1262,7 +1266,7 @@ and user site directory. The C standard streams (ex: ``stdout``) and the
LC_CTYPE locale are left unchanged. Signal handlers are not installed. LC_CTYPE locale are left unchanged. Signal handlers are not installed.
Configuration files are still used with this configuration. Set the Configuration files are still used with this configuration. Set the
:ref:`Path Configuration <init-path-config>` ("output fields") to ignore these :ref:`Python Path Configuration <init-path-config>` ("output fields") to ignore these
configuration files and avoid the function computing the default path configuration files and avoid the function computing the default path
configuration. configuration.
@ -1287,8 +1291,8 @@ and :ref:`Python UTF-8 Mode <utf8-mode>`
.. _init-path-config: .. _init-path-config:
Path Configuration Python Path Configuration
================== =========================
:c:type:`PyConfig` contains multiple fields for the path configuration: :c:type:`PyConfig` contains multiple fields for the path configuration:

View File

@ -493,6 +493,14 @@ Porting to Python 3.10
``unicodedata.ucnhash_CAPI`` has been moved to the internal C API. ``unicodedata.ucnhash_CAPI`` has been moved to the internal C API.
(Contributed by Victor Stinner in :issue:`42157`.) (Contributed by Victor Stinner in :issue:`42157`.)
* :c:func:`Py_GetPath`, :c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`,
:c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome` and
:c:func:`Py_GetProgramName` functions now return ``NULL`` if called before
:c:func:`Py_Initialize` (before Python is initialized). Use the new
:ref:`Python Initialization Configuration API <init-config>` to get the
:ref:`Python Path Configuration. <init-path-config>`.
(Contributed by Victor Stinner in :issue:`42260`.)
Deprecated Deprecated
---------- ----------

View File

@ -151,7 +151,9 @@ PyAPI_FUNC(void) _PyConfig_InitCompatConfig(PyConfig *config);
extern PyStatus _PyConfig_Copy( extern PyStatus _PyConfig_Copy(
PyConfig *config, PyConfig *config,
const PyConfig *config2); const PyConfig *config2);
extern PyStatus _PyConfig_InitPathConfig(PyConfig *config); extern PyStatus _PyConfig_InitPathConfig(
PyConfig *config,
int compute_path_config);
extern PyStatus _PyConfig_Read(PyConfig *config, int compute_path_config); extern PyStatus _PyConfig_Read(PyConfig *config, int compute_path_config);
extern PyStatus _PyConfig_Write(const PyConfig *config, extern PyStatus _PyConfig_Write(const PyConfig *config,
struct pyruntimestate *runtime); struct pyruntimestate *runtime);

View File

@ -0,0 +1,7 @@
:c:func:`Py_GetPath`, :c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`,
:c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome` and
:c:func:`Py_GetProgramName` functions now return ``NULL`` if called before
:c:func:`Py_Initialize` (before Python is initialized). Use the new
:ref:`Python Initialization Configuration API <init-config>` to get the
:ref:`Python Path Configuration. <init-path-config>`. Patch by Victor
Stinner.

View File

@ -2069,8 +2069,8 @@ config_read(PyConfig *config, int compute_path_config)
} }
} }
if (compute_path_config && config->_install_importlib) { if (config->_install_importlib) {
status = _PyConfig_InitPathConfig(config); status = _PyConfig_InitPathConfig(config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }

View File

@ -332,7 +332,8 @@ config_init_module_search_paths(PyConfig *config, _PyPathConfig *pathconfig)
- _PyPathConfig_Calculate() - _PyPathConfig_Calculate()
*/ */
static PyStatus static PyStatus
pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config) pathconfig_init(_PyPathConfig *pathconfig, const PyConfig *config,
int compute_path_config)
{ {
PyStatus status; PyStatus status;
@ -349,12 +350,9 @@ pathconfig_calculate(_PyPathConfig *pathconfig, const PyConfig *config)
goto done; goto done;
} }
if (_Py_path_config.module_search_path == NULL) { if (compute_path_config) {
status = _PyPathConfig_Calculate(pathconfig, config); status = _PyPathConfig_Calculate(pathconfig, config);
} }
else {
/* Py_SetPath() has been called: avoid _PyPathConfig_Calculate() */
}
done: done:
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
@ -363,17 +361,19 @@ done:
static PyStatus static PyStatus
config_calculate_pathconfig(PyConfig *config) config_init_pathconfig(PyConfig *config, int compute_path_config)
{ {
_PyPathConfig pathconfig = _PyPathConfig_INIT; _PyPathConfig pathconfig = _PyPathConfig_INIT;
PyStatus status; PyStatus status;
status = pathconfig_calculate(&pathconfig, config); status = pathconfig_init(&pathconfig, config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
goto done; goto done;
} }
if (!config->module_search_paths_set) { if (!config->module_search_paths_set
&& pathconfig.module_search_path != NULL)
{
status = config_init_module_search_paths(config, &pathconfig); status = config_init_module_search_paths(config, &pathconfig);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
goto done; goto done;
@ -381,7 +381,7 @@ config_calculate_pathconfig(PyConfig *config)
} }
#define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \ #define COPY_ATTR(PATH_ATTR, CONFIG_ATTR) \
if (config->CONFIG_ATTR == NULL) { \ if (config->CONFIG_ATTR == NULL && pathconfig.PATH_ATTR != NULL) { \
if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \ if (copy_wstr(&config->CONFIG_ATTR, pathconfig.PATH_ATTR) < 0) { \
goto no_memory; \ goto no_memory; \
} \ } \
@ -427,7 +427,7 @@ done:
PyStatus PyStatus
_PyConfig_InitPathConfig(PyConfig *config) _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
{ {
/* Do we need to calculate the path? */ /* Do we need to calculate the path? */
if (!config->module_search_paths_set if (!config->module_search_paths_set
@ -435,26 +435,26 @@ _PyConfig_InitPathConfig(PyConfig *config)
|| config->prefix == NULL || config->prefix == NULL
|| config->exec_prefix == NULL) || config->exec_prefix == NULL)
{ {
PyStatus status = config_calculate_pathconfig(config); PyStatus status = config_init_pathconfig(config, compute_path_config);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }
} }
if (config->base_prefix == NULL) { if (config->base_prefix == NULL && config->prefix != NULL) {
if (copy_wstr(&config->base_prefix, config->prefix) < 0) { if (copy_wstr(&config->base_prefix, config->prefix) < 0) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
} }
} }
if (config->base_exec_prefix == NULL) { if (config->base_exec_prefix == NULL && config->exec_prefix != NULL) {
if (copy_wstr(&config->base_exec_prefix, if (copy_wstr(&config->base_exec_prefix,
config->exec_prefix) < 0) { config->exec_prefix) < 0) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
} }
} }
if (config->base_executable == NULL) { if (config->base_executable == NULL && config->executable != NULL) {
if (copy_wstr(&config->base_executable, if (copy_wstr(&config->base_executable,
config->executable) < 0) { config->executable) < 0) {
return _PyStatus_NO_MEMORY(); return _PyStatus_NO_MEMORY();
@ -465,53 +465,6 @@ _PyConfig_InitPathConfig(PyConfig *config)
} }
static PyStatus
pathconfig_global_read(_PyPathConfig *pathconfig)
{
PyConfig config;
_PyConfig_InitCompatConfig(&config);
/* Call _PyConfig_InitPathConfig() */
PyStatus status = PyConfig_Read(&config);
if (_PyStatus_EXCEPTION(status)) {
goto done;
}
status = pathconfig_set_from_config(pathconfig, &config);
done:
PyConfig_Clear(&config);
return status;
}
static void
pathconfig_global_init(void)
{
PyStatus status;
if (_Py_path_config.module_search_path == NULL) {
status = pathconfig_global_read(&_Py_path_config);
if (_PyStatus_EXCEPTION(status)) {
Py_ExitStatusException(status);
}
}
else {
/* Global configuration already initialized */
}
assert(_Py_path_config.program_full_path != NULL);
assert(_Py_path_config.prefix != NULL);
assert(_Py_path_config.exec_prefix != NULL);
assert(_Py_path_config.module_search_path != NULL);
assert(_Py_path_config.program_name != NULL);
/* home can be NULL */
#ifdef MS_WINDOWS
assert(_Py_path_config.base_executable != NULL);
#endif
}
/* External interface */ /* External interface */
static void _Py_NO_RETURN static void _Py_NO_RETURN
@ -531,23 +484,17 @@ Py_SetPath(const wchar_t *path)
PyMemAllocatorEx old_alloc; PyMemAllocatorEx old_alloc;
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
/* Getting the program full path calls pathconfig_global_init() */
wchar_t *program_full_path = _PyMem_RawWcsdup(Py_GetProgramFullPath());
PyMem_RawFree(_Py_path_config.program_full_path);
PyMem_RawFree(_Py_path_config.prefix); PyMem_RawFree(_Py_path_config.prefix);
PyMem_RawFree(_Py_path_config.exec_prefix); PyMem_RawFree(_Py_path_config.exec_prefix);
PyMem_RawFree(_Py_path_config.module_search_path); PyMem_RawFree(_Py_path_config.module_search_path);
_Py_path_config.program_full_path = program_full_path;
_Py_path_config.prefix = _PyMem_RawWcsdup(L""); _Py_path_config.prefix = _PyMem_RawWcsdup(L"");
_Py_path_config.exec_prefix = _PyMem_RawWcsdup(L""); _Py_path_config.exec_prefix = _PyMem_RawWcsdup(L"");
_Py_path_config.module_search_path = _PyMem_RawWcsdup(path); _Py_path_config.module_search_path = _PyMem_RawWcsdup(path);
PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
if (_Py_path_config.program_full_path == NULL if (_Py_path_config.prefix == NULL
|| _Py_path_config.prefix == NULL
|| _Py_path_config.exec_prefix == NULL || _Py_path_config.exec_prefix == NULL
|| _Py_path_config.module_search_path == NULL) || _Py_path_config.module_search_path == NULL)
{ {
@ -621,7 +568,6 @@ _Py_SetProgramFullPath(const wchar_t *program_full_path)
wchar_t * wchar_t *
Py_GetPath(void) Py_GetPath(void)
{ {
pathconfig_global_init();
return _Py_path_config.module_search_path; return _Py_path_config.module_search_path;
} }
@ -629,7 +575,6 @@ Py_GetPath(void)
wchar_t * wchar_t *
Py_GetPrefix(void) Py_GetPrefix(void)
{ {
pathconfig_global_init();
return _Py_path_config.prefix; return _Py_path_config.prefix;
} }
@ -637,7 +582,6 @@ Py_GetPrefix(void)
wchar_t * wchar_t *
Py_GetExecPrefix(void) Py_GetExecPrefix(void)
{ {
pathconfig_global_init();
return _Py_path_config.exec_prefix; return _Py_path_config.exec_prefix;
} }
@ -645,7 +589,6 @@ Py_GetExecPrefix(void)
wchar_t * wchar_t *
Py_GetProgramFullPath(void) Py_GetProgramFullPath(void)
{ {
pathconfig_global_init();
return _Py_path_config.program_full_path; return _Py_path_config.program_full_path;
} }
@ -653,7 +596,6 @@ Py_GetProgramFullPath(void)
wchar_t* wchar_t*
Py_GetPythonHome(void) Py_GetPythonHome(void)
{ {
pathconfig_global_init();
return _Py_path_config.home; return _Py_path_config.home;
} }
@ -661,7 +603,6 @@ Py_GetPythonHome(void)
wchar_t * wchar_t *
Py_GetProgramName(void) Py_GetProgramName(void)
{ {
pathconfig_global_init();
return _Py_path_config.program_name; return _Py_path_config.program_name;
} }

View File

@ -1039,7 +1039,7 @@ init_interp_main(PyThreadState *tstate)
} }
// Compute the path configuration // Compute the path configuration
status = _PyConfig_InitPathConfig(&interp->config); status = _PyConfig_InitPathConfig(&interp->config, 1);
if (_PyStatus_EXCEPTION(status)) { if (_PyStatus_EXCEPTION(status)) {
return status; return status;
} }