GH-126985: move pyvenv.cfg detection from site to getpath (#126987)

This commit is contained in:
Filipe Laíns 🇵🇸 2024-11-26 13:46:33 +00:00 committed by GitHub
parent ab237ff81d
commit 2b0e2b2893
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 176 additions and 155 deletions

View file

@ -1590,9 +1590,22 @@ If a ``._pth`` file is present:
* Set :c:member:`~PyConfig.site_import` to ``0``. * Set :c:member:`~PyConfig.site_import` to ``0``.
* Set :c:member:`~PyConfig.safe_path` to ``1``. * Set :c:member:`~PyConfig.safe_path` to ``1``.
If :c:member:`~PyConfig.home` is not set and a ``pyvenv.cfg`` file is present in
the same directory as :c:member:`~PyConfig.executable`, or its parent,
:c:member:`~PyConfig.prefix` and :c:member:`~PyConfig.exec_prefix` are set that
location. When this happens, :c:member:`~PyConfig.base_prefix` and
:c:member:`~PyConfig.base_exec_prefix` still keep their value, pointing to the
base installation. See :ref:`sys-path-init-virtual-environments` for more
information.
The ``__PYVENV_LAUNCHER__`` environment variable is used to set The ``__PYVENV_LAUNCHER__`` environment variable is used to set
:c:member:`PyConfig.base_executable`. :c:member:`PyConfig.base_executable`.
.. versionchanged:: 3.14
:c:member:`~PyConfig.prefix`, and :c:member:`~PyConfig.exec_prefix`, are now
set to the ``pyvenv.cfg`` directory. This was previously done by :mod:`site`,
therefore affected by :option:`-S`.
.. _pyinitconfig_api: .. _pyinitconfig_api:

View file

@ -49,14 +49,22 @@ added path for configuration files.
identified by the "t" suffix in the version-specific directory name, such as identified by the "t" suffix in the version-specific directory name, such as
:file:`lib/python3.13t/`. :file:`lib/python3.13t/`.
If a file named "pyvenv.cfg" exists one directory above sys.executable, .. versionchanged:: 3.14
sys.prefix and sys.exec_prefix are set to that directory and
it is also checked for site-packages (sys.base_prefix and :mod:`site` is no longer responsible for updating :data:`sys.prefix` and
sys.base_exec_prefix will always be the "real" prefixes of the Python :data:`sys.exec_prefix` on :ref:`sys-path-init-virtual-environments`. This is
installation). If "pyvenv.cfg" (a bootstrap configuration file) contains now done during the :ref:`path initialization <sys-path-init>`. As a result,
the key "include-system-site-packages" set to anything other than "true" under :ref:`sys-path-init-virtual-environments`, :data:`sys.prefix` and
(case-insensitive), the system-level prefixes will not be :data:`sys.exec_prefix` no longer depend on the :mod:`site` initialization,
searched for site-packages; otherwise they will. and are therefore unaffected by :option:`-S`.
.. _site-virtual-environments-configuration:
When running under a :ref:`virtual environment <sys-path-init-virtual-environments>`,
the ``pyvenv.cfg`` file in :data:`sys.prefix` is checked for site-specific
configurations. If the ``include-system-site-packages`` key exists and is set to
``true`` (case-insensitive), the system-level prefixes will be searched for
site-packages, otherwise they won't.
.. index:: .. index::
single: # (hash); comment single: # (hash); comment

View file

@ -130,27 +130,26 @@ always available.
.. data:: base_exec_prefix .. data:: base_exec_prefix
Set during Python startup, before ``site.py`` is run, to the same value as Equivalent to :data:`exec_prefix`, but refering to the base Python installation.
:data:`exec_prefix`. If not running in a
:ref:`virtual environment <venv-def>`, the values will stay the same; if When running under :ref:`sys-path-init-virtual-environments`,
``site.py`` finds that a virtual environment is in use, the values of :data:`exec_prefix` gets overwritten to the virtual environment prefix.
:data:`prefix` and :data:`exec_prefix` will be changed to point to the :data:`base_exec_prefix`, conversely, does not change, and always points to
virtual environment, whereas :data:`base_prefix` and the base Python installation.
:data:`base_exec_prefix` will remain pointing to the base Python Refer to :ref:`sys-path-init-virtual-environments` for more information.
installation (the one which the virtual environment was created from).
.. versionadded:: 3.3 .. versionadded:: 3.3
.. data:: base_prefix .. data:: base_prefix
Set during Python startup, before ``site.py`` is run, to the same value as Equivalent to :data:`prefix`, but refering to the base Python installation.
:data:`prefix`. If not running in a :ref:`virtual environment <venv-def>`, the values
will stay the same; if ``site.py`` finds that a virtual environment is in When running under :ref:`virtual environment <venv-def>`,
use, the values of :data:`prefix` and :data:`exec_prefix` will be changed to :data:`prefix` gets overwritten to the virtual environment prefix.
point to the virtual environment, whereas :data:`base_prefix` and :data:`base_prefix`, conversely, does not change, and always points to
:data:`base_exec_prefix` will remain pointing to the base Python the base Python installation.
installation (the one which the virtual environment was created from). Refer to :ref:`sys-path-init-virtual-environments` for more information.
.. versionadded:: 3.3 .. versionadded:: 3.3
@ -483,11 +482,19 @@ always available.
.. note:: .. note::
If a :ref:`virtual environment <venv-def>` is in effect, this If a :ref:`virtual environment <venv-def>` is in effect, this :data:`exec_prefix`
value will be changed in ``site.py`` to point to the virtual environment. will point to the virtual environment. The value for the Python installation
The value for the Python installation will still be available, via will still be available, via :data:`base_exec_prefix`.
:data:`base_exec_prefix`. Refer to :ref:`sys-path-init-virtual-environments` for more information.
.. versionchanged:: 3.14
When running under a :ref:`virtual environment <venv-def>`,
:data:`prefix` and :data:`exec_prefix` are now set to the virtual
environment prefix by the :ref:`path initialization <sys-path-init>`,
instead of :mod:`site`. This means that :data:`prefix` and
:data:`exec_prefix` always point to the virtual environment, even when
:mod:`site` is disabled (:option:`-S`).
.. data:: executable .. data:: executable
@ -1483,10 +1490,21 @@ always available.
argument to the :program:`configure` script. See argument to the :program:`configure` script. See
:ref:`installation_paths` for derived paths. :ref:`installation_paths` for derived paths.
.. note:: If a :ref:`virtual environment <venv-def>` is in effect, this .. note::
value will be changed in ``site.py`` to point to the virtual
environment. The value for the Python installation will still be If a :ref:`virtual environment <venv-def>` is in effect, this :data:`prefix`
available, via :data:`base_prefix`. will point to the virtual environment. The value for the Python installation
will still be available, via :data:`base_prefix`.
Refer to :ref:`sys-path-init-virtual-environments` for more information.
.. versionchanged:: 3.14
When running under a :ref:`virtual environment <venv-def>`,
:data:`prefix` and :data:`exec_prefix` are now set to the virtual
environment prefix by the :ref:`path initialization <sys-path-init>`,
instead of :mod:`site`. This means that :data:`prefix` and
:data:`exec_prefix` always point to the virtual environment, even when
:mod:`site` is disabled (:option:`-S`).
.. data:: ps1 .. data:: ps1

View file

@ -47,8 +47,15 @@ however on other platforms :file:`lib/python{majorversion}.{minorversion}/lib-dy
``exec_prefix``. On some platforms :file:`lib` may be :file:`lib64` or another value, ``exec_prefix``. On some platforms :file:`lib` may be :file:`lib64` or another value,
see :data:`sys.platlibdir` and :envvar:`PYTHONPLATLIBDIR`. see :data:`sys.platlibdir` and :envvar:`PYTHONPLATLIBDIR`.
Once found, ``prefix`` and ``exec_prefix`` are available at :data:`sys.prefix` and Once found, ``prefix`` and ``exec_prefix`` are available at
:data:`sys.exec_prefix` respectively. :data:`sys.base_prefix` and :data:`sys.base_exec_prefix` respectively.
If :envvar:`PYTHONHOME` is not set, and a ``pyvenv.cfg`` file is found alongside
the main executable, or in its parent directory, :data:`sys.prefix` and
:data:`sys.exec_prefix` get set to the directory containing ``pyvenv.cfg``,
otherwise they are set to the same value as :data:`sys.base_prefix` and
:data:`sys.base_exec_prefix`, respectively.
This is used by :ref:`sys-path-init-virtual-environments`.
Finally, the :mod:`site` module is processed and :file:`site-packages` directories Finally, the :mod:`site` module is processed and :file:`site-packages` directories
are added to the module search path. A common way to customize the search path is are added to the module search path. A common way to customize the search path is
@ -60,18 +67,40 @@ the :mod:`site` module documentation.
Certain command line options may further affect path calculations. Certain command line options may further affect path calculations.
See :option:`-E`, :option:`-I`, :option:`-s` and :option:`-S` for further details. See :option:`-E`, :option:`-I`, :option:`-s` and :option:`-S` for further details.
Virtual environments .. versionchanged:: 3.14
:data:`sys.prefix` and :data:`sys.exec_prefix` are now set to the
``pyvenv.cfg`` directory during the path initialization. This was previously
done by :mod:`site`, therefore affected by :option:`-S`.
.. _sys-path-init-virtual-environments:
Virtual Environments
-------------------- --------------------
If Python is run in a virtual environment (as described at :ref:`tut-venv`) Virtual environments place a ``pyvenv.cfg`` file in their prefix, which causes
then ``prefix`` and ``exec_prefix`` are specific to the virtual environment. :data:`sys.prefix` and :data:`sys.exec_prefix` to point to them, instead of the
base installation.
If a ``pyvenv.cfg`` file is found alongside the main executable, or in the The ``prefix`` and ``exec_prefix`` values of the base installation are available
directory one level above the executable, the following variations apply: at :data:`sys.base_prefix` and :data:`sys.base_exec_prefix`.
* If ``home`` is an absolute path and :envvar:`PYTHONHOME` is not set, this As well as being used as a marker to identify virtual environments,
path is used instead of the path to the main executable when deducing ``prefix`` ``pyvenv.cfg`` may also be used to configure the :mod:`site` initialization.
and ``exec_prefix``. Please refer to :mod:`site`'s
:ref:`virtual environments documentation <site-virtual-environments-configuration>`.
.. note::
:envvar:`PYTHONHOME` overrides the ``pyvenv.cfg`` detection.
.. note::
There are other ways how "virtual environments" could be implemented, this
documentation referes implementations based on the ``pyvenv.cfg`` mechanism,
such as :mod:`venv`. Most virtual environment implementations follow the
model set by :mod:`venv`, but there may be exotic implementations that
diverge from it.
_pth files _pth files
---------- ----------

View file

@ -25,6 +25,9 @@ A virtual environment is created on top of an existing
Python installation, known as the virtual environment's "base" Python, and may Python installation, known as the virtual environment's "base" Python, and may
optionally be isolated from the packages in the base environment, optionally be isolated from the packages in the base environment,
so only those explicitly installed in the virtual environment are available. so only those explicitly installed in the virtual environment are available.
See :ref:`sys-path-init-virtual-environments` and :mod:`site`'s
:ref:`virtual environments documentation <site-virtual-environments-configuration>`
for more information.
When used from within a virtual environment, common installation tools such as When used from within a virtual environment, common installation tools such as
:pypi:`pip` will install Python packages into a virtual environment :pypi:`pip` will install Python packages into a virtual environment

View file

@ -94,6 +94,12 @@ def _trace(message):
print(message, file=sys.stderr) print(message, file=sys.stderr)
def _warn(*args, **kwargs):
import warnings
warnings.warn(*args, **kwargs)
def makepath(*paths): def makepath(*paths):
dir = os.path.join(*paths) dir = os.path.join(*paths)
try: try:
@ -619,7 +625,10 @@ def venv(known_paths):
elif key == 'home': elif key == 'home':
sys._home = value sys._home = value
sys.prefix = sys.exec_prefix = site_prefix if sys.prefix != site_prefix:
_warn(f'Unexpected value in sys.prefix, expected {site_prefix}, got {sys.prefix}', RuntimeWarning)
if sys.exec_prefix != site_prefix:
_warn(f'Unexpected value in sys.exec_prefix, expected {site_prefix}, got {sys.exec_prefix}', RuntimeWarning)
# Doing this here ensures venv takes precedence over user-site # Doing this here ensures venv takes precedence over user-site
addsitepackages(known_paths, [sys.prefix]) addsitepackages(known_paths, [sys.prefix])

View file

@ -173,7 +173,9 @@ _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
_PY_VERSION = sys.version.split()[0] _PY_VERSION = sys.version.split()[0]
_PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}' _PY_VERSION_SHORT = f'{sys.version_info[0]}.{sys.version_info[1]}'
_PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}' _PY_VERSION_SHORT_NO_DOT = f'{sys.version_info[0]}{sys.version_info[1]}'
_PREFIX = os.path.normpath(sys.prefix)
_BASE_PREFIX = os.path.normpath(sys.base_prefix) _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) _BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
# Mutex guarding initialization of _CONFIG_VARS. # Mutex guarding initialization of _CONFIG_VARS.
_CONFIG_VARS_LOCK = threading.RLock() _CONFIG_VARS_LOCK = threading.RLock()
@ -465,10 +467,8 @@ def _init_config_vars():
# Normalized versions of prefix and exec_prefix are handy to have; # Normalized versions of prefix and exec_prefix are handy to have;
# in fact, these are the standard versions used most places in the # in fact, these are the standard versions used most places in the
# Distutils. # Distutils.
_PREFIX = os.path.normpath(sys.prefix) _CONFIG_VARS['prefix'] = _PREFIX
_EXEC_PREFIX = os.path.normpath(sys.exec_prefix) _CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX
_CONFIG_VARS['prefix'] = _PREFIX # FIXME: This gets overwriten by _init_posix.
_CONFIG_VARS['exec_prefix'] = _EXEC_PREFIX # FIXME: This gets overwriten by _init_posix.
_CONFIG_VARS['py_version'] = _PY_VERSION _CONFIG_VARS['py_version'] = _PY_VERSION
_CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT _CONFIG_VARS['py_version_short'] = _PY_VERSION_SHORT
_CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT _CONFIG_VARS['py_version_nodot'] = _PY_VERSION_SHORT_NO_DOT
@ -541,7 +541,6 @@ def get_config_vars(*args):
With arguments, return a list of values that result from looking up With arguments, return a list of values that result from looking up
each argument in the configuration variable dictionary. each argument in the configuration variable dictionary.
""" """
global _CONFIG_VARS_INITIALIZED
# Avoid claiming the lock once initialization is complete. # Avoid claiming the lock once initialization is complete.
if not _CONFIG_VARS_INITIALIZED: if not _CONFIG_VARS_INITIALIZED:
@ -552,15 +551,6 @@ def get_config_vars(*args):
# don't re-enter init_config_vars(). # don't re-enter init_config_vars().
if _CONFIG_VARS is None: if _CONFIG_VARS is None:
_init_config_vars() _init_config_vars()
else:
# If the site module initialization happened after _CONFIG_VARS was
# initialized, a virtual environment might have been activated, resulting in
# variables like sys.prefix changing their value, so we need to re-init the
# config vars (see GH-126789).
if _CONFIG_VARS['base'] != os.path.normpath(sys.prefix):
with _CONFIG_VARS_LOCK:
_CONFIG_VARS_INITIALIZED = False
_init_config_vars()
if args: if args:
vals = [] vals = []

View file

@ -1649,14 +1649,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
config = { config = {
'base_prefix': sysconfig.get_config_var("prefix"), 'base_prefix': sysconfig.get_config_var("prefix"),
'base_exec_prefix': exec_prefix, 'base_exec_prefix': exec_prefix,
'exec_prefix': exec_prefix, 'exec_prefix': tmpdir,
'prefix': tmpdir,
'base_executable': base_executable, 'base_executable': base_executable,
'executable': executable, 'executable': executable,
'module_search_paths': paths, 'module_search_paths': paths,
} }
if MS_WINDOWS: if MS_WINDOWS:
config['base_prefix'] = pyvenv_home config['base_prefix'] = pyvenv_home
config['prefix'] = pyvenv_home
config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib') config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
config['use_frozen_modules'] = bool(not support.Py_DEBUG) config['use_frozen_modules'] = bool(not support.Py_DEBUG)
else: else:

View file

@ -92,8 +92,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable=r"C:\venv\Scripts\python.exe", executable=r"C:\venv\Scripts\python.exe",
prefix=r"C:\Python", prefix=r"C:\venv",
exec_prefix=r"C:\Python", exec_prefix=r"C:\venv",
base_executable=r"C:\Python\python.exe", base_executable=r"C:\Python\python.exe",
base_prefix=r"C:\Python", base_prefix=r"C:\Python",
base_exec_prefix=r"C:\Python", base_exec_prefix=r"C:\Python",
@ -339,8 +339,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable="/venv/bin/python", executable="/venv/bin/python",
prefix="/usr", prefix="/venv",
exec_prefix="/usr", exec_prefix="/venv",
base_executable="/usr/bin/python", base_executable="/usr/bin/python",
base_prefix="/usr", base_prefix="/usr",
base_exec_prefix="/usr", base_exec_prefix="/usr",
@ -371,8 +371,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable="/venv/bin/python", executable="/venv/bin/python",
prefix="/usr", prefix="/venv",
exec_prefix="/usr", exec_prefix="/venv",
base_executable="/usr/bin/python3", base_executable="/usr/bin/python3",
base_prefix="/usr", base_prefix="/usr",
base_exec_prefix="/usr", base_exec_prefix="/usr",
@ -404,8 +404,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable="/venv/bin/python", executable="/venv/bin/python",
prefix="/path/to/non-installed", prefix="/venv",
exec_prefix="/path/to/non-installed", exec_prefix="/venv",
base_executable="/path/to/non-installed/bin/python", base_executable="/path/to/non-installed/bin/python",
base_prefix="/path/to/non-installed", base_prefix="/path/to/non-installed",
base_exec_prefix="/path/to/non-installed", base_exec_prefix="/path/to/non-installed",
@ -435,8 +435,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable="/venv/bin/python", executable="/venv/bin/python",
prefix="/usr", prefix="/venv",
exec_prefix="/usr", exec_prefix="/venv",
base_executable="/usr/bin/python9", base_executable="/usr/bin/python9",
base_prefix="/usr", base_prefix="/usr",
base_exec_prefix="/usr", base_exec_prefix="/usr",
@ -652,8 +652,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable=f"{venv_path}/bin/python", executable=f"{venv_path}/bin/python",
prefix="/Library/Frameworks/Python.framework/Versions/9.8", prefix=venv_path,
exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8", exec_prefix=venv_path,
base_executable="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8", base_executable="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8",
base_prefix="/Library/Frameworks/Python.framework/Versions/9.8", base_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
base_exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8", base_exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
@ -697,8 +697,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable=f"{venv_path}/bin/python", executable=f"{venv_path}/bin/python",
prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8", prefix=venv_path,
exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8", exec_prefix=venv_path,
base_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8", base_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8",
base_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8", base_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
base_exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8", base_exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
@ -734,8 +734,8 @@ class MockGetPathTests(unittest.TestCase):
]) ])
expected = dict( expected = dict(
executable="/framework/Python9.8/python", executable="/framework/Python9.8/python",
prefix="/usr", prefix="/framework/Python9.8",
exec_prefix="/usr", exec_prefix="/framework/Python9.8",
base_executable="/usr/bin/python", base_executable="/usr/bin/python",
base_prefix="/usr", base_prefix="/usr",
base_exec_prefix="/usr", base_exec_prefix="/usr",

View file

@ -110,6 +110,7 @@ class TestSysConfig(unittest.TestCase):
**venv_create_args, **venv_create_args,
) )
def test_get_path_names(self): def test_get_path_names(self):
self.assertEqual(get_path_names(), sysconfig._SCHEME_KEYS) self.assertEqual(get_path_names(), sysconfig._SCHEME_KEYS)
@ -591,71 +592,6 @@ class TestSysConfig(unittest.TestCase):
suffix = sysconfig.get_config_var('EXT_SUFFIX') suffix = sysconfig.get_config_var('EXT_SUFFIX')
self.assertTrue(suffix.endswith('-darwin.so'), suffix) self.assertTrue(suffix.endswith('-darwin.so'), suffix)
@requires_subprocess()
def test_config_vars_depend_on_site_initialization(self):
script = textwrap.dedent("""
import sysconfig
config_vars = sysconfig.get_config_vars()
import json
print(json.dumps(config_vars, indent=2))
""")
with self.venv() as venv:
site_config_vars = json.loads(venv.run('-c', script).stdout)
no_site_config_vars = json.loads(venv.run('-S', '-c', script).stdout)
self.assertNotEqual(site_config_vars, no_site_config_vars)
# With the site initialization, the virtual environment should be enabled.
self.assertEqual(site_config_vars['base'], venv.prefix)
self.assertEqual(site_config_vars['platbase'], venv.prefix)
#self.assertEqual(site_config_vars['prefix'], venv.prefix) # # FIXME: prefix gets overwriten by _init_posix
# Without the site initialization, the virtual environment should be disabled.
self.assertEqual(no_site_config_vars['base'], site_config_vars['installed_base'])
self.assertEqual(no_site_config_vars['platbase'], site_config_vars['installed_platbase'])
@requires_subprocess()
def test_config_vars_recalculation_after_site_initialization(self):
script = textwrap.dedent("""
import sysconfig
before = sysconfig.get_config_vars()
import site
site.main()
after = sysconfig.get_config_vars()
import json
print(json.dumps({'before': before, 'after': after}, indent=2))
""")
with self.venv() as venv:
config_vars = json.loads(venv.run('-S', '-c', script).stdout)
self.assertNotEqual(config_vars['before'], config_vars['after'])
self.assertEqual(config_vars['after']['base'], venv.prefix)
#self.assertEqual(config_vars['after']['prefix'], venv.prefix) # FIXME: prefix gets overwriten by _init_posix
#self.assertEqual(config_vars['after']['exec_prefix'], venv.prefix) # FIXME: exec_prefix gets overwriten by _init_posix
@requires_subprocess()
def test_paths_depend_on_site_initialization(self):
script = textwrap.dedent("""
import sysconfig
paths = sysconfig.get_paths()
import json
print(json.dumps(paths, indent=2))
""")
with self.venv() as venv:
site_paths = json.loads(venv.run('-c', script).stdout)
no_site_paths = json.loads(venv.run('-S', '-c', script).stdout)
self.assertNotEqual(site_paths, no_site_paths)
@requires_subprocess() @requires_subprocess()
def test_makefile_overwrites_config_vars(self): def test_makefile_overwrites_config_vars(self):
script = textwrap.dedent(""" script = textwrap.dedent("""
@ -689,7 +625,6 @@ class TestSysConfig(unittest.TestCase):
self.assertNotEqual(data['prefix'], data['base_prefix']) self.assertNotEqual(data['prefix'], data['base_prefix'])
self.assertNotEqual(data['exec_prefix'], data['base_exec_prefix']) self.assertNotEqual(data['exec_prefix'], data['base_exec_prefix'])
class MakefileTests(unittest.TestCase): class MakefileTests(unittest.TestCase):
@unittest.skipIf(sys.platform.startswith('win'), @unittest.skipIf(sys.platform.startswith('win'),

View file

@ -0,0 +1,3 @@
When running under a virtual environment with the :mod:`site` disabled (see
:option:`-S`), :data:`sys.prefix` and :data:`sys.base_prefix` will now point
to the virtual environment, instead of the base installation.

View file

@ -640,11 +640,20 @@ else:
# For a venv, update the main prefix/exec_prefix but leave the base ones unchanged # For a venv, update the main prefix/exec_prefix but leave the base ones unchanged
# XXX: We currently do not update prefix here, but it happens in site.py if venv_prefix:
#if venv_prefix: if not base_prefix:
# base_prefix = prefix base_prefix = prefix
# base_exec_prefix = exec_prefix if not base_exec_prefix:
# prefix = exec_prefix = venv_prefix base_exec_prefix = exec_prefix
prefix = exec_prefix = venv_prefix
# After calculating prefix and exec_prefix, use their values for base_prefix and
# base_exec_prefix if they haven't been set.
if not base_prefix:
base_prefix = prefix
if not base_exec_prefix:
base_exec_prefix = exec_prefix
# ****************************************************************************** # ******************************************************************************
@ -679,7 +688,7 @@ elif not pythonpath_was_set:
# QUIRK: POSIX uses the default prefix when in the build directory # QUIRK: POSIX uses the default prefix when in the build directory
pythonpath.append(joinpath(PREFIX, ZIP_LANDMARK)) pythonpath.append(joinpath(PREFIX, ZIP_LANDMARK))
else: else:
pythonpath.append(joinpath(prefix, ZIP_LANDMARK)) pythonpath.append(joinpath(base_prefix, ZIP_LANDMARK))
if os_name == 'nt' and use_environment and winreg: if os_name == 'nt' and use_environment and winreg:
# QUIRK: Windows also lists paths in the registry. Paths are stored # QUIRK: Windows also lists paths in the registry. Paths are stored
@ -714,13 +723,13 @@ elif not pythonpath_was_set:
# Then add any entries compiled into the PYTHONPATH macro. # Then add any entries compiled into the PYTHONPATH macro.
if PYTHONPATH: if PYTHONPATH:
for p in PYTHONPATH.split(DELIM): for p in PYTHONPATH.split(DELIM):
pythonpath.append(joinpath(prefix, p)) pythonpath.append(joinpath(base_prefix, p))
# Then add stdlib_dir and platstdlib_dir # Then add stdlib_dir and platstdlib_dir
if not stdlib_dir and prefix: if not stdlib_dir and base_prefix:
stdlib_dir = joinpath(prefix, STDLIB_SUBDIR) stdlib_dir = joinpath(base_prefix, STDLIB_SUBDIR)
if not platstdlib_dir and exec_prefix: if not platstdlib_dir and base_exec_prefix:
platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK) platstdlib_dir = joinpath(base_exec_prefix, PLATSTDLIB_LANDMARK)
if os_name == 'nt': if os_name == 'nt':
# QUIRK: Windows generates paths differently # QUIRK: Windows generates paths differently
@ -750,9 +759,13 @@ elif not pythonpath_was_set:
# QUIRK: Non-Windows replaces prefix/exec_prefix with defaults when running # QUIRK: Non-Windows replaces prefix/exec_prefix with defaults when running
# in build directory. This happens after pythonpath calculation. # in build directory. This happens after pythonpath calculation.
# Virtual environments using the build directory Python still keep their prefix.
if os_name != 'nt' and build_prefix: if os_name != 'nt' and build_prefix:
if not venv_prefix:
prefix = config.get('prefix') or PREFIX prefix = config.get('prefix') or PREFIX
exec_prefix = config.get('exec_prefix') or EXEC_PREFIX or prefix exec_prefix = config.get('exec_prefix') or EXEC_PREFIX or prefix
base_prefix = config.get('base_prefix') or PREFIX
base_exec_prefix = config.get('base_exec_prefix') or EXEC_PREFIX or base_prefix
# ****************************************************************************** # ******************************************************************************
@ -788,8 +801,8 @@ config['executable'] = executable
config['base_executable'] = base_executable config['base_executable'] = base_executable
config['prefix'] = prefix config['prefix'] = prefix
config['exec_prefix'] = exec_prefix config['exec_prefix'] = exec_prefix
config['base_prefix'] = base_prefix or prefix config['base_prefix'] = base_prefix
config['base_exec_prefix'] = base_exec_prefix or exec_prefix config['base_exec_prefix'] = base_exec_prefix
config['platlibdir'] = platlibdir config['platlibdir'] = platlibdir
# test_embed expects empty strings, not None # test_embed expects empty strings, not None