-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
gh-92452: Avoid race in initialization of sysconfig._CONFIG_VARS #92453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
import os | ||
import sys | ||
import threading | ||
from os.path import pardir, realpath | ||
|
||
__all__ = [ | ||
|
@@ -172,7 +173,11 @@ def joinuser(*args): | |
_BASE_PREFIX = os.path.normpath(sys.base_prefix) | ||
_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) | ||
_BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix) | ||
# Mutex guarding initialization of _CONFIG_VARS. | ||
_CONFIG_VARS_LOCK = threading.RLock() | ||
_CONFIG_VARS = None | ||
# True iff _CONFIG_VARS has been fully initialized. | ||
_CONFIG_VARS_INITIALIZED = False | ||
_USER_BASE = None | ||
|
||
# Regexes needed for parsing Makefile (and similar syntaxes, | ||
|
@@ -625,6 +630,71 @@ def get_path(name, scheme=get_default_scheme(), vars=None, expand=True): | |
return get_paths(scheme, vars, expand)[name] | ||
|
||
|
||
def _init_config_vars(): | ||
global _CONFIG_VARS | ||
_CONFIG_VARS = {} | ||
# Normalized versions of prefix and exec_prefix are handy to have; | ||
# in fact, these are the standard versions used most places in the | ||
# Distutils. | ||
_CONFIG_VARS['prefix'] = _PREFIX | ||
_CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX | ||
_CONFIG_VARS['py_version'] = _PY_VERSION | ||
_CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT | ||
_CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT | ||
_CONFIG_VARS['installed_base'] = _BASE_PREFIX | ||
_CONFIG_VARS['base'] = _PREFIX | ||
_CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX | ||
_CONFIG_VARS['platbase'] = _EXEC_PREFIX | ||
_CONFIG_VARS['projectbase'] = _PROJECT_BASE | ||
_CONFIG_VARS['platlibdir'] = sys.platlibdir | ||
try: | ||
_CONFIG_VARS['abiflags'] = sys.abiflags | ||
except AttributeError: | ||
# sys.abiflags may not be defined on all platforms. | ||
_CONFIG_VARS['abiflags'] = '' | ||
try: | ||
_CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '') | ||
except AttributeError: | ||
_CONFIG_VARS['py_version_nodot_plat'] = '' | ||
|
||
if os.name == 'nt': | ||
_init_non_posix(_CONFIG_VARS) | ||
_CONFIG_VARS['VPATH'] = sys._vpath | ||
if os.name == 'posix': | ||
_init_posix(_CONFIG_VARS) | ||
if _HAS_USER_BASE: | ||
# Setting 'userbase' is done below the call to the | ||
# init function to enable using 'get_config_var' in | ||
# the init-function. | ||
_CONFIG_VARS['userbase'] = _getuserbase() | ||
|
||
# Always convert srcdir to an absolute path | ||
srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) | ||
if os.name == 'posix': | ||
if _PYTHON_BUILD: | ||
# If srcdir is a relative path (typically '.' or '..') | ||
# then it should be interpreted relative to the directory | ||
# containing Makefile. | ||
base = os.path.dirname(get_makefile_filename()) | ||
srcdir = os.path.join(base, srcdir) | ||
else: | ||
# srcdir is not meaningful since the installation is | ||
# spread about the filesystem. We choose the | ||
# directory containing the Makefile since we know it | ||
# exists. | ||
srcdir = os.path.dirname(get_makefile_filename()) | ||
_CONFIG_VARS['srcdir'] = _safe_realpath(srcdir) | ||
|
||
# OS X platforms require special customization to handle | ||
# multi-architecture, multi-os-version installers | ||
if sys.platform == 'darwin': | ||
import _osx_support | ||
_osx_support.customize_config_vars(_CONFIG_VARS) | ||
|
||
global _CONFIG_VARS_INITIALIZED | ||
_CONFIG_VARS_INITIALIZED = True | ||
|
||
|
||
def get_config_vars(*args): | ||
"""With no arguments, return a dictionary of all configuration | ||
variables relevant for the current platform. | ||
|
@@ -635,66 +705,16 @@ def get_config_vars(*args): | |
With arguments, return a list of values that result from looking up | ||
each argument in the configuration variable dictionary. | ||
""" | ||
global _CONFIG_VARS | ||
if _CONFIG_VARS is None: | ||
_CONFIG_VARS = {} | ||
# Normalized versions of prefix and exec_prefix are handy to have; | ||
# in fact, these are the standard versions used most places in the | ||
# Distutils. | ||
_CONFIG_VARS['prefix'] = _PREFIX | ||
_CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX | ||
_CONFIG_VARS['py_version'] = _PY_VERSION | ||
_CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT | ||
_CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT | ||
_CONFIG_VARS['installed_base'] = _BASE_PREFIX | ||
_CONFIG_VARS['base'] = _PREFIX | ||
_CONFIG_VARS['installed_platbase'] = _BASE_EXEC_PREFIX | ||
_CONFIG_VARS['platbase'] = _EXEC_PREFIX | ||
_CONFIG_VARS['projectbase'] = _PROJECT_BASE | ||
_CONFIG_VARS['platlibdir'] = sys.platlibdir | ||
try: | ||
_CONFIG_VARS['abiflags'] = sys.abiflags | ||
except AttributeError: | ||
# sys.abiflags may not be defined on all platforms. | ||
_CONFIG_VARS['abiflags'] = '' | ||
try: | ||
_CONFIG_VARS['py_version_nodot_plat'] = sys.winver.replace('.', '') | ||
except AttributeError: | ||
_CONFIG_VARS['py_version_nodot_plat'] = '' | ||
|
||
if os.name == 'nt': | ||
_init_non_posix(_CONFIG_VARS) | ||
_CONFIG_VARS['VPATH'] = sys._vpath | ||
if os.name == 'posix': | ||
_init_posix(_CONFIG_VARS) | ||
if _HAS_USER_BASE: | ||
# Setting 'userbase' is done below the call to the | ||
# init function to enable using 'get_config_var' in | ||
# the init-function. | ||
_CONFIG_VARS['userbase'] = _getuserbase() | ||
|
||
# Always convert srcdir to an absolute path | ||
srcdir = _CONFIG_VARS.get('srcdir', _PROJECT_BASE) | ||
if os.name == 'posix': | ||
if _PYTHON_BUILD: | ||
# If srcdir is a relative path (typically '.' or '..') | ||
# then it should be interpreted relative to the directory | ||
# containing Makefile. | ||
base = os.path.dirname(get_makefile_filename()) | ||
srcdir = os.path.join(base, srcdir) | ||
else: | ||
# srcdir is not meaningful since the installation is | ||
# spread about the filesystem. We choose the | ||
# directory containing the Makefile since we know it | ||
# exists. | ||
srcdir = os.path.dirname(get_makefile_filename()) | ||
_CONFIG_VARS['srcdir'] = _safe_realpath(srcdir) | ||
|
||
# OS X platforms require special customization to handle | ||
# multi-architecture, multi-os-version installers | ||
if sys.platform == 'darwin': | ||
import _osx_support | ||
_osx_support.customize_config_vars(_CONFIG_VARS) | ||
|
||
# Avoid claiming the lock once initialization is complete. | ||
if not _CONFIG_VARS_INITIALIZED: | ||
with _CONFIG_VARS_LOCK: | ||
# Test again with the lock held to avoid races. Note that | ||
# we test _CONFIG_VARS here, not _CONFIG_VARS_INITIALIZED, | ||
# to ensure that recursive calls to get_config_vars() | ||
# don't re-enter init_config_vars(). | ||
if _CONFIG_VARS is None: | ||
_init_config_vars() | ||
Comment on lines
+715
to
+717
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name of the function was changed to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is minor, I think everyone will understand what the comment is saying, but feel free to open a PR updating it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will keep that line in mind for replies to comments on future pull requests. |
||
|
||
if args: | ||
vals = [] | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Fixed a race condition that could cause :func:`sysconfig.get_config_var` to | ||
incorrectly return :const:`None` in multi-threaded programs. |
Uh oh!
There was an error while loading. Please reload this page.