|
| 1 | +PEP: 838 |
| 2 | +Title: Adding python-version to pyvenv.cfg |
| 3 | +Author: Konstantin Schütze <konstin@mailbox.org> |
| 4 | +Sponsor: Alex Waygood <alex.waygood@gmail.com> |
| 5 | +PEP-Delegate: Paul Moore <p.f.moore@gmail.com> |
| 6 | +Discussions-To: Pending |
| 7 | +Status: Draft |
| 8 | +Type: Standards Track |
| 9 | +Topic: Packaging |
| 10 | +Created: 15-Jul-2026 |
| 11 | +Python-Version: 3.16 |
| 12 | +Post-History: Pending |
| 13 | + |
| 14 | + |
| 15 | +Abstract |
| 16 | +======== |
| 17 | + |
| 18 | +This PEP proposes adding a ``python-version`` field to ``pyvenv.cfg`` |
| 19 | +which records the Python interpreter used by a virtual environment. |
| 20 | +It records only the major and minor version to be resilient to patch |
| 21 | +version updates. |
| 22 | + |
| 23 | + |
| 24 | +Motivation |
| 25 | +========== |
| 26 | + |
| 27 | +``pyvenv.cfg``, as defined by :pep:`405`, only specifies the ``home`` |
| 28 | +and ``include-system-site-packages`` keys. Different tools record different |
| 29 | +information about the Python version of a virtual environment. For a virtual |
| 30 | +environment created with Python 3.14.4 final: |
| 31 | + |
| 32 | +- CPython's :mod:`venv` module adds a ``version`` field as |
| 33 | + ``'%d.%d.%d' % sys.version_info[:3]`` |
| 34 | + (`CPython source <https://github.com/python/cpython/blob/bc235304dfe5f018a87e1d73ca1ad2912f4ab999/Lib/venv/__init__.py#L234>`__). |
| 35 | + Example: ``version = 3.14.4``. |
| 36 | +- The ``virtualenv`` package adds ``version_info`` and ``version`` fields as |
| 37 | + ``".".join(str(i) for i in sys.version_info)`` and |
| 38 | + ``".".join(str(i) for i in sys.version_info[:3])`` |
| 39 | + (`virtualenv source <https://github.com/pypa/virtualenv/blob/21.5.1/src/virtualenv/create/creator.py#L222-L227>`__). |
| 40 | + Example: ``version_info = 3.14.4.final.0`` and ``version = 3.14.4``. |
| 41 | +- uv adds a ``version_info`` field based on |
| 42 | + ``platform.python_version()`` |
| 43 | + (`uv source <https://github.com/astral-sh/uv/blob/7bbc01af54e8fd1d3c61d05b65f233574f7466da/crates/uv-virtualenv/src/virtualenv.rs#L531>`__). |
| 44 | + Example: ``version_info = 3.14.4``. |
| 45 | + |
| 46 | +This information is used by a variety of tools, see :ref:`appendix`. Some of |
| 47 | +them want to present the full Python version to the user for identification, |
| 48 | +while others only need the major and minor version of Python. |
| 49 | + |
| 50 | +One problem is the different definitions of ``version`` and |
| 51 | +``version_info``, and the lack of guarantees for downstream tools. There is |
| 52 | +no guarantee that either field is present, nor is either field's granularity |
| 53 | +defined. |
| 54 | + |
| 55 | +Another problem is that the Python interpreter underneath the virtual |
| 56 | +environment may change. Linux distributions routinely update CPython patch |
| 57 | +versions as part of regular updates within a single distribution version. uv |
| 58 | +supports `upgrading existing Python installations <https://docs.astral.sh/uv/guides/install-python/#upgrading-python-versions>`__, |
| 59 | +upgrading virtual environments using the interpreter in the process. A |
| 60 | +``version_info`` key in ``pyvenv.cfg`` with a patch version and a potential |
| 61 | +prerelease component goes stale this way. |
| 62 | + |
| 63 | +Tools interacting with virtual environments can `fail after a Python |
| 64 | +patch-version upgrade <https://github.com/astral-sh/uv/issues/19920>`__ when |
| 65 | +their assumptions about version granularity are violated. A standardized field |
| 66 | +with only major and minor version avoids this problem. |
| 67 | + |
| 68 | + |
| 69 | +Specification |
| 70 | +============= |
| 71 | + |
| 72 | +A new ``python-version`` field is added to ``pyvenv.cfg``. It contains a |
| 73 | +string value with the major and minor version of the Python interpreter, such |
| 74 | +as ``3.16``. The value can be obtained with |
| 75 | +``f"{sys.version_info[0]}.{sys.version_info[1]}"``. Tools creating virtual |
| 76 | +environments MUST write ``python-version`` to ``pyvenv.cfg``. Reading |
| 77 | +``version`` or ``version_info`` is discouraged. |
| 78 | + |
| 79 | +A Python interpreter MAY refuse to run from a virtual environment with a |
| 80 | +mismatching ``python-version``. |
| 81 | + |
| 82 | + |
| 83 | +Rationale |
| 84 | +========= |
| 85 | + |
| 86 | +The ``python-version`` key is not yet used by any known tool (`GitHub code |
| 87 | +search <https://github.com/search?q=path%3A**%2Fpyvenv.cfg%20python-version&type=code>`__), |
| 88 | +avoiding breakage in tools that read any of the existing keys. By |
| 89 | +specifying only the major and minor version, the value remains fresh even if |
| 90 | +the underlying Python interpreter is updated from one patch release to |
| 91 | +another. |
| 92 | + |
| 93 | +This PEP does not handle the case of ``python-version`` going stale due to |
| 94 | +the underlying Python interpreter being updated to another minor version. |
| 95 | +This may happen when upgrading from one Linux distribution version to another. |
| 96 | +Such an update breaks any packages using CPython's unstable C API and cannot |
| 97 | +be fixed through a different way of recording values in ``pyvenv.cfg``; it can |
| 98 | +only be fixed by regenerating the virtual environment, resolving with the new |
| 99 | +Python version and installing the appropriate packages. By performing a check |
| 100 | +during interpreter startup, we avoid inscrutable errors at import time or at |
| 101 | +runtime, and can inform the user about the problem. |
| 102 | + |
| 103 | +To show accurate Python version information to the user, tools can present the |
| 104 | +value of ``python-version``, or if they need the full Python version, they can |
| 105 | +query the interpreter for ``sys.version_info``, invalidating this information |
| 106 | +when the virtual environment interpreter or the underlying base executable |
| 107 | +change. This PEP does not require any specific tool behavior, nor does it |
| 108 | +disallow existing patterns. Its goal is to provide the required information |
| 109 | +for correct, resilient implementations. |
| 110 | + |
| 111 | + |
| 112 | +Backwards Compatibility |
| 113 | +======================= |
| 114 | + |
| 115 | +If ``python-version`` is not available, tools can fall back to the |
| 116 | +existing unspecified fields or inspect the Python interpreter. There is no |
| 117 | +known existing usage of ``python-version`` in ``pyvenv.cfg``. |
| 118 | + |
| 119 | + |
| 120 | +How to Teach This |
| 121 | +================= |
| 122 | + |
| 123 | +A new specification page for ``pyvenv.cfg`` will be added to the `Python |
| 124 | +Packaging User Guide <https://packaging.python.org/>`__, including |
| 125 | +documentation for this field. The field is not user-facing. |
| 126 | + |
| 127 | + |
| 128 | +Reference Implementation |
| 129 | +======================== |
| 130 | + |
| 131 | +Reference implementations will be provided for uv, virtualenv, and CPython. |
| 132 | + |
| 133 | + |
| 134 | +.. _appendix: |
| 135 | + |
| 136 | +Appendix: Existing Tool Behavior |
| 137 | +================================ |
| 138 | + |
| 139 | +A non-exhaustive list of how tools parse version values: |
| 140 | + |
| 141 | +- **ty** splits the dotted value and parses only the major and minor |
| 142 | + components, ignoring any remaining components |
| 143 | + (`ty source <https://github.com/astral-sh/ruff/blob/1b011de418f1007a37fbe33f989a47bf99e2aa9f/crates/ty_site_packages/src/lib.rs#L789-L803>`__). |
| 144 | + Ty only needs the major and minor version of Python. |
| 145 | +- The **VS Code Python extension** parses both fields and |
| 146 | + separately handles virtualenv-style values such as ``3.9.0.final.0``. |
| 147 | + When both fields are present, it selects the most specific version |
| 148 | + (`VS Code source <https://github.com/microsoft/vscode-python/blob/34348154b5eac877eef60b5b838699c6cbaf58b6/src/client/pythonEnvironments/common/environmentManagers/simplevirtualenvs.ts#L123-L211>`__). |
| 149 | + VS Code presents the full Python version to the user for identification. |
| 150 | +- **Microsoft Python Environment Tools** requires at least three numeric |
| 151 | + components for both fields, then parses the first two components |
| 152 | + (`Python Environment Tools source <https://github.com/microsoft/python-environment-tools/blob/89bf1c02290e09413e745cbbb0194bc50491bf0d/crates/pet-core/src/pyvenv_cfg.rs#L10-L162>`__). |
| 153 | + Its uv-specific parser stores only ``version_info`` |
| 154 | + (`Python Environment Tools uv source <https://github.com/microsoft/python-environment-tools/blob/89bf1c02290e09413e745cbbb0194bc50491bf0d/crates/pet-uv/src/lib.rs#L27-L60>`__). |
| 155 | + VS Code presents the full Python version to the user for identification. |
| 156 | +- **pre-commit** compares ``version_info`` with the ``sys.version_info`` |
| 157 | + components joined by dots. |
| 158 | + (`pre-commit source <https://github.com/pre-commit/pre-commit/blob/1553b465fd7ea42321ae0d04d1b41e706b89ae45/pre_commit/languages/python.py#L27-L33>`__, |
| 159 | + `health-check source <https://github.com/pre-commit/pre-commit/blob/1553b465fd7ea42321ae0d04d1b41e706b89ae45/pre_commit/languages/python.py#L175-L211>`__). |
| 160 | + `This caused a failure <https://github.com/astral-sh/uv/issues/19920>`__ where uv and pre-commit disagreed about virtual environment freshness checks. |
| 161 | +- **Jute** reads ``version_info`` |
| 162 | + (`Jute source <https://github.com/ekzhang/jute/blob/18723a036b843d9efc1d07b326bda5614b2020e7/src-tauri/src/commands/venv.rs#L125-L165>`__). |
| 163 | + Jute presents the full Python version to the user for identification. |
| 164 | +- **MediaHarbor** parses the first two components of ``version`` |
| 165 | + (`MediaHarbor source <https://github.com/MediaHarbor/mediaharbor/blob/0fe6810d3a348e238e7d5ed87f6c2a9b16510b85/src/core/src/venv_manager.rs#L174-L184>`__). |
| 166 | +- **Jac** parses the first two components of ``version`` |
| 167 | + (`Jac source <https://github.com/jaseci-labs/jaseci/blob/e7afd1d8253a940bca989b946a1569970f99f719/jac/jaclang/project/impl/dependencies.impl.jac#L437-L453>`__). |
| 168 | + |
| 169 | + |
| 170 | +Copyright |
| 171 | +========= |
| 172 | + |
| 173 | +This document is placed in the public domain or under the |
| 174 | +CC0-1.0-Universal license, whichever is more permissive. |
0 commit comments