Skip to content

Commit 49d35ed

Browse files
committed
PEP 837: Adding python-version to pyvenv.cfg
1 parent 8618d76 commit 49d35ed

2 files changed

Lines changed: 175 additions & 0 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ peps/pep-0832.rst @brettcannon
711711
peps/pep-0833.rst @dstufft
712712
peps/pep-0835.rst @ilevkivskyi
713713
peps/pep-0836.rst @savannahostrowski @Fidget-Spinner @brandtbucher
714+
peps/pep-0838.rst @AlexWaygood
714715
# ...
715716
peps/pep-2026.rst @hugovk
716717
# ...

peps/pep-0838.rst

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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

Comments
 (0)