]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
GH-126985: move pyvenv.cfg detection from site to getpath (#126987)
authorFilipe Laíns 🇵🇸 <lains@riseup.net>
Tue, 26 Nov 2024 13:46:33 +0000 (13:46 +0000)
committerGitHub <noreply@github.com>
Tue, 26 Nov 2024 13:46:33 +0000 (13:46 +0000)
12 files changed:
Doc/c-api/init_config.rst
Doc/library/site.rst
Doc/library/sys.rst
Doc/library/sys_path_init.rst
Doc/library/venv.rst
Lib/site.py
Lib/sysconfig/__init__.py
Lib/test/test_embed.py
Lib/test/test_getpath.py
Lib/test/test_sysconfig.py
Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst [new file with mode: 0644]
Modules/getpath.py

index e621545cffcee35356144af6eea5015fea5f18d4..d6569ddcf586fa5d48867472b01f782fbacf373d 100644 (file)
@@ -1590,9 +1590,22 @@ If a ``._pth`` file is present:
 * Set :c:member:`~PyConfig.site_import` to ``0``.
 * 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
 :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:
 
index 4508091f679dc778e53748c682096968553e8968..5f2a0f610e1aa54afca0bcb6c85192da601b5a9e 100644 (file)
@@ -49,14 +49,22 @@ added path for configuration files.
    identified by the "t" suffix in the version-specific directory name, such as
    :file:`lib/python3.13t/`.
 
-If a file named "pyvenv.cfg" exists one directory above sys.executable,
-sys.prefix and sys.exec_prefix are set to that directory and
-it is also checked for site-packages (sys.base_prefix and
-sys.base_exec_prefix will always be the "real" prefixes of the Python
-installation). If "pyvenv.cfg" (a bootstrap configuration file) contains
-the key "include-system-site-packages" set to anything other than "true"
-(case-insensitive), the system-level prefixes will not be
-searched for site-packages; otherwise they will.
+.. versionchanged:: 3.14
+
+   :mod:`site` is no longer responsible for updating :data:`sys.prefix` and
+   :data:`sys.exec_prefix` on :ref:`sys-path-init-virtual-environments`. This is
+   now done during the :ref:`path initialization <sys-path-init>`. As a result,
+   under :ref:`sys-path-init-virtual-environments`, :data:`sys.prefix` and
+   :data:`sys.exec_prefix` no longer depend on the :mod:`site` initialization,
+   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::
    single: # (hash); comment
index 773e5d4185dfaf883c23471fb7a2cd6371a5cdf2..dd6293c722e7ad8d4cc1cb559f7d4addf522e5a2 100644 (file)
@@ -130,27 +130,26 @@ always available.
 
 .. data:: base_exec_prefix
 
-   Set during Python startup, before ``site.py`` is run, to the same value as
-   :data:`exec_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 use, the values of
-   :data:`prefix` and :data:`exec_prefix` will be changed to point to the
-   virtual environment, whereas :data:`base_prefix` and
-   :data:`base_exec_prefix` will remain pointing to the base Python
-   installation (the one which the virtual environment was created from).
+   Equivalent to :data:`exec_prefix`, but refering to the base Python installation.
+
+   When running under :ref:`sys-path-init-virtual-environments`,
+   :data:`exec_prefix` gets overwritten to the virtual environment prefix.
+   :data:`base_exec_prefix`, conversely, does not change, and always points to
+   the base Python installation.
+   Refer to :ref:`sys-path-init-virtual-environments` for more information.
 
    .. versionadded:: 3.3
 
 
 .. data:: base_prefix
 
-   Set during Python startup, before ``site.py`` is run, to the same value as
-   :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
-   use, the values of :data:`prefix` and :data:`exec_prefix` will be changed to
-   point to the virtual environment, whereas :data:`base_prefix` and
-   :data:`base_exec_prefix` will remain pointing to the base Python
-   installation (the one which the virtual environment was created from).
+   Equivalent to :data:`prefix`, but refering to the base Python installation.
+
+   When running under :ref:`virtual environment <venv-def>`,
+   :data:`prefix` gets overwritten to the virtual environment prefix.
+   :data:`base_prefix`, conversely, does not change, and always points to
+   the base Python installation.
+   Refer to :ref:`sys-path-init-virtual-environments` for more information.
 
    .. versionadded:: 3.3
 
@@ -483,11 +482,19 @@ always available.
 
    .. note::
 
-      If a :ref:`virtual environment <venv-def>` is in effect, this
-      value will be changed in ``site.py`` to point to the virtual environment.
-      The value for the Python installation will still be available, via
-      :data:`base_exec_prefix`.
+      If a :ref:`virtual environment <venv-def>` is in effect, this :data:`exec_prefix`
+      will point to the virtual environment. The value for the Python installation
+      will still be available, via :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
 
@@ -1483,10 +1490,21 @@ always available.
    argument to the :program:`configure` script.  See
    :ref:`installation_paths` for derived paths.
 
-   .. note:: If a :ref:`virtual environment <venv-def>` is in effect, this
-      value will be changed in ``site.py`` to point to the virtual
-      environment. The value for the Python installation will still be
-      available, via :data:`base_prefix`.
+   .. note::
+
+      If a :ref:`virtual environment <venv-def>` is in effect, this :data:`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
index a87a41cf829fa89ed69dd00838943c6f01ee8a03..18fe32d9c7f10a3a2976d80c496a23db5d737456 100644 (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,
 see :data:`sys.platlibdir` and :envvar:`PYTHONPLATLIBDIR`.
 
-Once found, ``prefix`` and ``exec_prefix`` are available at :data:`sys.prefix` and
-:data:`sys.exec_prefix` respectively.
+Once found, ``prefix`` and ``exec_prefix`` are available at
+: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
 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.
    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`)
-then ``prefix`` and ``exec_prefix`` are specific to the virtual environment.
+Virtual environments place a ``pyvenv.cfg`` file in their prefix, which causes
+:data:`sys.prefix` and :data:`sys.exec_prefix` to point to them, instead of the
+base installation.
+
+The ``prefix`` and ``exec_prefix`` values of the base installation are available
+at :data:`sys.base_prefix` and :data:`sys.base_exec_prefix`.
 
-If a ``pyvenv.cfg`` file is found alongside the main executable, or in the
-directory one level above the executable, the following variations apply:
+As well as being used as a marker to identify virtual environments,
+``pyvenv.cfg`` may also be used to configure the :mod:`site` initialization.
+Please refer to :mod:`site`'s
+:ref:`virtual environments documentation <site-virtual-environments-configuration>`.
+
+.. note::
+
+   :envvar:`PYTHONHOME` overrides the ``pyvenv.cfg`` detection.
+
+.. note::
 
-* If ``home`` is an absolute path and :envvar:`PYTHONHOME` is not set, this
-  path is used instead of the path to the main executable when deducing ``prefix``
-  and ``exec_prefix``.
+   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
 ----------
index 5205c6c211d9bf915b426d8cbcd587f8665e1d71..bed799aedfdfb118a4f08aeebbe47b9c5c185b97 100644 (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
 optionally be isolated from the packages in the base environment,
 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
 :pypi:`pip` will install Python packages into a virtual environment
index abf4b5202445544528939083ee5820e8e85eb8fa..92bd1ccdadd924ace61240f24262d63e02ee1a32 100644 (file)
@@ -94,6 +94,12 @@ def _trace(message):
         print(message, file=sys.stderr)
 
 
+def _warn(*args, **kwargs):
+    import warnings
+
+    warnings.warn(*args, **kwargs)
+
+
 def makepath(*paths):
     dir = os.path.join(*paths)
     try:
@@ -619,7 +625,10 @@ def venv(known_paths):
                     elif key == 'home':
                         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
         addsitepackages(known_paths, [sys.prefix])
index 67a071963d8c7d54c66501a73604a88512db0d2b..ee52700b51fd071aea76bc189fd69c38086466c3 100644 (file)
@@ -173,7 +173,9 @@ _SCHEME_KEYS = ('stdlib', 'platstdlib', 'purelib', 'platlib', 'include',
 _PY_VERSION = sys.version.split()[0]
 _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]}'
+_PREFIX = os.path.normpath(sys.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)
 # Mutex guarding initialization of _CONFIG_VARS.
 _CONFIG_VARS_LOCK = threading.RLock()
@@ -465,10 +467,8 @@ def _init_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.
-    _PREFIX = os.path.normpath(sys.prefix)
-    _EXEC_PREFIX = os.path.normpath(sys.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['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
@@ -541,7 +541,6 @@ 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_INITIALIZED
 
     # Avoid claiming the lock once initialization is complete.
     if not _CONFIG_VARS_INITIALIZED:
@@ -552,15 +551,6 @@ def get_config_vars(*args):
             # don't re-enter init_config_vars().
             if _CONFIG_VARS is None:
                 _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:
         vals = []
index bf861ef06ee2d3fb0376f42d758137dc6c273e86..5c38b28322deb4fb60e30c6b70e303b4d7fb5f62 100644 (file)
@@ -1649,14 +1649,14 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
             config = {
                 'base_prefix': sysconfig.get_config_var("prefix"),
                 'base_exec_prefix': exec_prefix,
-                'exec_prefix': exec_prefix,
+                'exec_prefix': tmpdir,
+                'prefix': tmpdir,
                 'base_executable': base_executable,
                 'executable': executable,
                 'module_search_paths': paths,
             }
             if MS_WINDOWS:
                 config['base_prefix'] = pyvenv_home
-                config['prefix'] = pyvenv_home
                 config['stdlib_dir'] = os.path.join(pyvenv_home, 'Lib')
                 config['use_frozen_modules'] = bool(not support.Py_DEBUG)
             else:
index d5dcdad9614ecc1076372aff217bf9b1099488e6..7e5c4a3d14ddc5f1465c542954764182e13132f6 100644 (file)
@@ -92,8 +92,8 @@ class MockGetPathTests(unittest.TestCase):
         ])
         expected = dict(
             executable=r"C:\venv\Scripts\python.exe",
-            prefix=r"C:\Python",
-            exec_prefix=r"C:\Python",
+            prefix=r"C:\venv",
+            exec_prefix=r"C:\venv",
             base_executable=r"C:\Python\python.exe",
             base_prefix=r"C:\Python",
             base_exec_prefix=r"C:\Python",
@@ -339,8 +339,8 @@ class MockGetPathTests(unittest.TestCase):
         ])
         expected = dict(
             executable="/venv/bin/python",
-            prefix="/usr",
-            exec_prefix="/usr",
+            prefix="/venv",
+            exec_prefix="/venv",
             base_executable="/usr/bin/python",
             base_prefix="/usr",
             base_exec_prefix="/usr",
@@ -371,8 +371,8 @@ class MockGetPathTests(unittest.TestCase):
         ])
         expected = dict(
             executable="/venv/bin/python",
-            prefix="/usr",
-            exec_prefix="/usr",
+            prefix="/venv",
+            exec_prefix="/venv",
             base_executable="/usr/bin/python3",
             base_prefix="/usr",
             base_exec_prefix="/usr",
@@ -404,8 +404,8 @@ class MockGetPathTests(unittest.TestCase):
         ])
         expected = dict(
             executable="/venv/bin/python",
-            prefix="/path/to/non-installed",
-            exec_prefix="/path/to/non-installed",
+            prefix="/venv",
+            exec_prefix="/venv",
             base_executable="/path/to/non-installed/bin/python",
             base_prefix="/path/to/non-installed",
             base_exec_prefix="/path/to/non-installed",
@@ -435,8 +435,8 @@ class MockGetPathTests(unittest.TestCase):
         ])
         expected = dict(
             executable="/venv/bin/python",
-            prefix="/usr",
-            exec_prefix="/usr",
+            prefix="/venv",
+            exec_prefix="/venv",
             base_executable="/usr/bin/python9",
             base_prefix="/usr",
             base_exec_prefix="/usr",
@@ -652,8 +652,8 @@ class MockGetPathTests(unittest.TestCase):
         ])
         expected = dict(
             executable=f"{venv_path}/bin/python",
-            prefix="/Library/Frameworks/Python.framework/Versions/9.8",
-            exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
+            prefix=venv_path,
+            exec_prefix=venv_path,
             base_executable="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8",
             base_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(
             executable=f"{venv_path}/bin/python",
-            prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
-            exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
+            prefix=venv_path,
+            exec_prefix=venv_path,
             base_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8",
             base_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(
             executable="/framework/Python9.8/python",
-            prefix="/usr",
-            exec_prefix="/usr",
+            prefix="/framework/Python9.8",
+            exec_prefix="/framework/Python9.8",
             base_executable="/usr/bin/python",
             base_prefix="/usr",
             base_exec_prefix="/usr",
index 9bbf8d0c6cf2da1289e3fa85117062e16bff6196..a705dd0cd89e4d33d457fb4ce3a9132b0bfb0bef 100644 (file)
@@ -110,6 +110,7 @@ class TestSysConfig(unittest.TestCase):
             **venv_create_args,
         )
 
+
     def test_get_path_names(self):
         self.assertEqual(get_path_names(), sysconfig._SCHEME_KEYS)
 
@@ -591,71 +592,6 @@ class TestSysConfig(unittest.TestCase):
         suffix = sysconfig.get_config_var('EXT_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()
     def test_makefile_overwrites_config_vars(self):
         script = textwrap.dedent("""
@@ -689,7 +625,6 @@ class TestSysConfig(unittest.TestCase):
         self.assertNotEqual(data['prefix'], data['base_prefix'])
         self.assertNotEqual(data['exec_prefix'], data['base_exec_prefix'])
 
-
 class MakefileTests(unittest.TestCase):
 
     @unittest.skipIf(sys.platform.startswith('win'),
diff --git a/Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst b/Misc/NEWS.d/next/Library/2024-11-18-23-42-06.gh-issue-126985.7XplY9.rst
new file mode 100644 (file)
index 0000000..c875c7b
--- /dev/null
@@ -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.
index 1c1eb6cbf3ee2223f78b6d08a0d776bea2a89533..7949fd813d0d076458cf412126764174d505d8e6 100644 (file)
@@ -640,11 +640,20 @@ else:
 
 
 # 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:
-#    base_prefix = prefix
-#    base_exec_prefix = exec_prefix
-#    prefix = exec_prefix = venv_prefix
+if venv_prefix:
+    if not base_prefix:
+        base_prefix = prefix
+    if not base_exec_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
         pythonpath.append(joinpath(PREFIX, ZIP_LANDMARK))
     else:
-        pythonpath.append(joinpath(prefix, ZIP_LANDMARK))
+        pythonpath.append(joinpath(base_prefix, ZIP_LANDMARK))
 
     if os_name == 'nt' and use_environment and winreg:
         # 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.
     if PYTHONPATH:
         for p in PYTHONPATH.split(DELIM):
-            pythonpath.append(joinpath(prefix, p))
+            pythonpath.append(joinpath(base_prefix, p))
 
     # Then add stdlib_dir and platstdlib_dir
-    if not stdlib_dir and prefix:
-        stdlib_dir = joinpath(prefix, STDLIB_SUBDIR)
-    if not platstdlib_dir and exec_prefix:
-        platstdlib_dir = joinpath(exec_prefix, PLATSTDLIB_LANDMARK)
+    if not stdlib_dir and base_prefix:
+        stdlib_dir = joinpath(base_prefix, STDLIB_SUBDIR)
+    if not platstdlib_dir and base_exec_prefix:
+        platstdlib_dir = joinpath(base_exec_prefix, PLATSTDLIB_LANDMARK)
 
     if os_name == 'nt':
         # 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
 # 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:
-    prefix = config.get('prefix') or PREFIX
-    exec_prefix = config.get('exec_prefix') or EXEC_PREFIX or prefix
+    if not venv_prefix:
+        prefix = config.get('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['prefix'] = prefix
 config['exec_prefix'] = exec_prefix
-config['base_prefix'] = base_prefix or prefix
-config['base_exec_prefix'] = base_exec_prefix or exec_prefix
+config['base_prefix'] = base_prefix
+config['base_exec_prefix'] = base_exec_prefix
 
 config['platlibdir'] = platlibdir
 # test_embed expects empty strings, not None