From 49d35ed31ecdd8df5a4dca009ea92ac111e7fee2 Mon Sep 17 00:00:00 2001 From: konsti Date: Wed, 15 Jul 2026 14:32:10 +0200 Subject: [PATCH] PEP 837: Adding python-version to pyvenv.cfg --- .github/CODEOWNERS | 1 + peps/pep-0838.rst | 174 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 peps/pep-0838.rst diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 509e23f3e1e..6c44270a533 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -711,6 +711,7 @@ peps/pep-0832.rst @brettcannon peps/pep-0833.rst @dstufft peps/pep-0835.rst @ilevkivskyi peps/pep-0836.rst @savannahostrowski @Fidget-Spinner @brandtbucher +peps/pep-0838.rst @AlexWaygood # ... peps/pep-2026.rst @hugovk # ... diff --git a/peps/pep-0838.rst b/peps/pep-0838.rst new file mode 100644 index 00000000000..0302cb086e2 --- /dev/null +++ b/peps/pep-0838.rst @@ -0,0 +1,174 @@ +PEP: 838 +Title: Adding python-version to pyvenv.cfg +Author: Konstantin Schütze +Sponsor: Alex Waygood +PEP-Delegate: Paul Moore +Discussions-To: Pending +Status: Draft +Type: Standards Track +Topic: Packaging +Created: 15-Jul-2026 +Python-Version: 3.16 +Post-History: Pending + + +Abstract +======== + +This PEP proposes adding a ``python-version`` field to ``pyvenv.cfg`` +which records the Python interpreter used by a virtual environment. +It records only the major and minor version to be resilient to patch +version updates. + + +Motivation +========== + +``pyvenv.cfg``, as defined by :pep:`405`, only specifies the ``home`` +and ``include-system-site-packages`` keys. Different tools record different +information about the Python version of a virtual environment. For a virtual +environment created with Python 3.14.4 final: + +- CPython's :mod:`venv` module adds a ``version`` field as + ``'%d.%d.%d' % sys.version_info[:3]`` + (`CPython source `__). + Example: ``version = 3.14.4``. +- The ``virtualenv`` package adds ``version_info`` and ``version`` fields as + ``".".join(str(i) for i in sys.version_info)`` and + ``".".join(str(i) for i in sys.version_info[:3])`` + (`virtualenv source `__). + Example: ``version_info = 3.14.4.final.0`` and ``version = 3.14.4``. +- uv adds a ``version_info`` field based on + ``platform.python_version()`` + (`uv source `__). + Example: ``version_info = 3.14.4``. + +This information is used by a variety of tools, see :ref:`appendix`. Some of +them want to present the full Python version to the user for identification, +while others only need the major and minor version of Python. + +One problem is the different definitions of ``version`` and +``version_info``, and the lack of guarantees for downstream tools. There is +no guarantee that either field is present, nor is either field's granularity +defined. + +Another problem is that the Python interpreter underneath the virtual +environment may change. Linux distributions routinely update CPython patch +versions as part of regular updates within a single distribution version. uv +supports `upgrading existing Python installations `__, +upgrading virtual environments using the interpreter in the process. A +``version_info`` key in ``pyvenv.cfg`` with a patch version and a potential +prerelease component goes stale this way. + +Tools interacting with virtual environments can `fail after a Python +patch-version upgrade `__ when +their assumptions about version granularity are violated. A standardized field +with only major and minor version avoids this problem. + + +Specification +============= + +A new ``python-version`` field is added to ``pyvenv.cfg``. It contains a +string value with the major and minor version of the Python interpreter, such +as ``3.16``. The value can be obtained with +``f"{sys.version_info[0]}.{sys.version_info[1]}"``. Tools creating virtual +environments MUST write ``python-version`` to ``pyvenv.cfg``. Reading +``version`` or ``version_info`` is discouraged. + +A Python interpreter MAY refuse to run from a virtual environment with a +mismatching ``python-version``. + + +Rationale +========= + +The ``python-version`` key is not yet used by any known tool (`GitHub code +search `__), +avoiding breakage in tools that read any of the existing keys. By +specifying only the major and minor version, the value remains fresh even if +the underlying Python interpreter is updated from one patch release to +another. + +This PEP does not handle the case of ``python-version`` going stale due to +the underlying Python interpreter being updated to another minor version. +This may happen when upgrading from one Linux distribution version to another. +Such an update breaks any packages using CPython's unstable C API and cannot +be fixed through a different way of recording values in ``pyvenv.cfg``; it can +only be fixed by regenerating the virtual environment, resolving with the new +Python version and installing the appropriate packages. By performing a check +during interpreter startup, we avoid inscrutable errors at import time or at +runtime, and can inform the user about the problem. + +To show accurate Python version information to the user, tools can present the +value of ``python-version``, or if they need the full Python version, they can +query the interpreter for ``sys.version_info``, invalidating this information +when the virtual environment interpreter or the underlying base executable +change. This PEP does not require any specific tool behavior, nor does it +disallow existing patterns. Its goal is to provide the required information +for correct, resilient implementations. + + +Backwards Compatibility +======================= + +If ``python-version`` is not available, tools can fall back to the +existing unspecified fields or inspect the Python interpreter. There is no +known existing usage of ``python-version`` in ``pyvenv.cfg``. + + +How to Teach This +================= + +A new specification page for ``pyvenv.cfg`` will be added to the `Python +Packaging User Guide `__, including +documentation for this field. The field is not user-facing. + + +Reference Implementation +======================== + +Reference implementations will be provided for uv, virtualenv, and CPython. + + +.. _appendix: + +Appendix: Existing Tool Behavior +================================ + +A non-exhaustive list of how tools parse version values: + +- **ty** splits the dotted value and parses only the major and minor + components, ignoring any remaining components + (`ty source `__). + Ty only needs the major and minor version of Python. +- The **VS Code Python extension** parses both fields and + separately handles virtualenv-style values such as ``3.9.0.final.0``. + When both fields are present, it selects the most specific version + (`VS Code source `__). + VS Code presents the full Python version to the user for identification. +- **Microsoft Python Environment Tools** requires at least three numeric + components for both fields, then parses the first two components + (`Python Environment Tools source `__). + Its uv-specific parser stores only ``version_info`` + (`Python Environment Tools uv source `__). + VS Code presents the full Python version to the user for identification. +- **pre-commit** compares ``version_info`` with the ``sys.version_info`` + components joined by dots. + (`pre-commit source `__, + `health-check source `__). + `This caused a failure `__ where uv and pre-commit disagreed about virtual environment freshness checks. +- **Jute** reads ``version_info`` + (`Jute source `__). + Jute presents the full Python version to the user for identification. +- **MediaHarbor** parses the first two components of ``version`` + (`MediaHarbor source `__). +- **Jac** parses the first two components of ``version`` + (`Jac source `__). + + +Copyright +========= + +This document is placed in the public domain or under the +CC0-1.0-Universal license, whichever is more permissive.