PEP: 832
Title: Virtual environment discovery
Author: Brett Cannon <brett@python.org>
Discussions-To: https://discuss.python.org/t/106998
Status: Draft
Type: Standards Track
Created: 19-Jan-2026
Python-Version: 3.15
Post-History: `15-Apr-2026 <https://discuss.python.org/t/106998>`__
              `23-Apr-2026 <https://discuss.python.org/t/106998/52>`__
              `31-Jul-2026 <https://discuss.python.org/t/106998/148>`__


Abstract
========

This PEP sets out to help make the discovery of a project's existing
environments easier. By providing a default location to look for a virtual
environment as already supported by most tools as well as an easy way for
workflow tools to list any other environments (virtual or not), tools will have
a way to find any and all existing environments for a project.


Motivation
==========

Imagine you are on your Mac laptop and you double-click your desktop shortcut
to launch Emacs (feel free to substitute "Mac" and "Emacs" with your preferred
OS and editor, respectively). You open the directory for your project in Emacs.
Now, how is Emacs (or any other tool for that matter) supposed to know where
the environments for your project are? There's no possible detection of an
activated virtual environment via the ``VIRTUAL_ENV`` environment variable
because you didn't launch it from a terminal. You potentially could scan all
subdirectories for a :file:`pyvenv.cfg` file to find a virtual environment, but
that assumes the virtual environments are kept locally with the project and
that there is only one of them, rather than several from which to choose.
As well, not all projects use virtual environments and may use a different
project isolation mechanism like conda environments.

What are tools like code editors, which need access to the environments being
used to provide functionality like auto-complete, to do when there is currently
no standardized way to tell anyone where any environments are? Currently,
tools like editors have to hard-code a search algorithm for every tool that
they choose to support. As well, they can document any conventions they
support, but that assumes you or the tool you use to manage your environments
follow those conventions, which, being conventions, are not written down
anywhere.

And this is not a hypothetical issue. The author of this PEP was the dev
manager for Python support in VS Code for 7 years and saw firsthand the
user struggles and constant feature requests for finding one's environments for
a project.

This issue is also not restricted to code editors. Other tools have a need to
access a project's environment to know what is installed. One example is
type checkers which need access to the packages that are installed to
appropriately gather type annotations for 3rd-party code in order to type check
the user's code.

The goal of this PEP is to provide a specification for tools which
create/manage environments a way to tell other tools where the environments for
a project are. And in the case of a project which has multiple environments,
this PEP is meant to allow for specifying the default environment to use so
users are not forced to make a choice of environment if they do not want to
make such a decision (e.g. at first launch of their code editor).

Please note this PEP neither condones nor discourages having multiple
environments for a single project; it is neutral as to whether having a single
environment or multiple ones is good or bad. It also does not condone one
environment type over another.

Specification
=============

This PEP does not define what the "root of a project" means, but the assumption
is that it is the directory one would open in their code editor to work on a
project's code. This could be the directory where the project's
:file:`pyproject.toml` lives, or potentially the top directory of a monorepo.

The virtual environment for a project MAY be a path named :file:`.venv`
(i.e. :file:`.venv/pyvenv.cfg` will exist, which can be used to detect the
existence of a virtual environment) in the root of the project. This PEP makes
no judgment whether :file:`.venv` is a physical or logical path to a directory
containing a virtual environment, nor whether logical paths should be resolved
to their physical equivalent before use.

The root of a project MAY have a :file:`.python-envs` file. This file acts as a
listing of all known environments for the project (sans :file:`.venv`; how that
and :file:`.python-envs` work together will be covered later). The
:file:`.python-envs` MUST be encoded using UTF-8. Each line of the file
represents an environment that is usable by the project and may be separated by
``\n`` or ``\r\n``. A trailing newline of either ``\n`` or ``\r\n`` is allowed
and MUST be ignored.

Lines in a :file:`.python-envs` file MAY be paths to an environment. Paths MAY
be relative, and if they are, they MUST be relative to the
:file:`.python-envs` file. IF a path is for a virtual environment, THEN the
path MUST be to the directory of the virtual environment (i.e. the directory
containing the :file:`pyvenv.cfg` file). A line MAY represent any type of an
environment. Tools reading a :file:`.python-envs` MAY choose what sort of
environments they support and thus MAY ignore any lines they do not
understand (although there is a specific restriction in regards to the default
environment not being supported; covered later). For environments a tool
understands but are somehow malformed (e.g. a virtual environment whose
symlinks no longer resolve), it is up to the tool to decide how to handle such
a situation. There are NO other restrictions on how environments are
represented or what type of environment is in a :file:`.python-envs` file. An
empty file has NO special meaning other than representing the lack of any
environments.

Duplicate lines MAY be in the file. Listing the same environment multiple times
does NOT carry any meaning. Any tool MAY remove duplicates at any point, but it
MUST maintain what environment is considered the default during de-duplication.

The last environment listed in a :file:`.python-envs` file MUST be considered
the default environment when a default environment is desired. IF a tool does
not support the last environment listed THEN the tool MUST either ask the user
which environment to use OR error out.

IF both a virtual environment in a :file:`.venv` directory path and a
:file:`.python-envs` file exist side-by-side, THEN the :file:`.venv` path
MUST be implicitly considered the last line in the :file:`.python-envs` file.
This also means the :file:`.venv` virtual environment is considered the default
virtual environment.

With regard to committing a :file:`.python-envs` file to version control, it
MAY be done when the location of the environment(s) is considered static
for a project once it is set up. For instance, some projects that use tox_ have
a "dev" environment defined in their configuration that ends up at
``.tox/dev``. Setting a :file:`.python-envs` file to point to that virtual
environment and checking in the file is reasonable. The same goes for a project
that is only worked on within a container where the location of the
environment is controlled and thus static on the file system. The guidance of
NOT committing your actual virtual environment to version control is unchanged
by this PEP.

Tools MAY use a file system locking mechanism to help guarantee no race
conditions when reading or writing to a :`.python-envs` file.


Rationale
=========

Explicitly supporting :file:`.venv` is to codify what's already a convention:

- `Poetry <https://python-poetry.org/docs/configuration#virtualenvsin-project>`__
  will detect a virtual environment in such a location,
- `PDM <https://pdm-project.org/en/latest/usage/venv/#virtualenv-auto-creation>`__
  creates virtual environments there already
- `uv <https://docs.astral.sh/uv/concepts/projects/layout/#the-project-environment>`__
  creates environments there already
- `Hatch can support <https://hatch.pypa.io/latest/blog/2022/10/08/hatch-v160/#virtual-environment-location>`__
  a virtual environment there)
- `VS Code <https://code.visualstudio.com/docs/python/environments#_creating-environments>`__
   will select it automatically, while still allowing configuration
- `PyCharm <https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html#env-requirements>`__
   will use it
- `GitHub <https://github.com/github/gitignore/blob/main/Python.gitignore>`__
  has a default :file:`.gitignore` which ignores :file:`.venv`
- `GitLab <https://gitlab.com/gitlab-org/gitlab/-/blob/master/vendor/gitignore/Python.gitignore>`__
  has a default :file:`.gitignore` which ignores :file:`.venv`
- `Codeberg <https://codeberg.org/forgejo/forgejo/src/branch/forgejo/options/gitignore/Python>`__
  has a default :file:`.gitignore` which ignores :file:`.venv`

But not every person or tool wants to keep an environment in the project or
even use the :file:`.venv` name. In those situations, you need _some_ way
to tell other tools where to find the environments. That's the purpose of the
:file:`.python-envs` file. The file itself is hidden as it isn't a critical
aspect of the project (environments themselves can be viewed as implementation
details). The file name was chosen to make sure it didn't clash with any other
tool using the same name while still being self-descriptive.

The :file:`.python-envs` file is specifically agnostic when it comes to what
type of environment can be represented. This helps future-proof the file for
unforeseen, future environments. As well, leaving the representation as loose
as what a single line of a file can represent helps with allowing alternative
environments that a tool may or may not support (which can include alternative
representations for virtual environments, e.g. connecting over SSH). It does
mean, though, that tools SHOULD check the line for appropriate use.

The file format is simple to allow for easy manipulation. Having a
line-delimited file format makes it easy to append a line to a
:file:`.python-envs` file via the terminal, e.g.:

- ``echo "<path>" >> .python-envs``
- ``Add-Content .python-envs "<path>"``
- ``python3 -c "import sys; p=sys.argv[1]; open('.python-envs', 'a').write(p)" "<path>"``

To make appending as simple a process as possible, duplicate lines are
allowed to occur in :file:`.python-envs`. This alleviates having to check the
file before appending. This is also why a trailing newline is allowed in the
file.

The file format is also simple to avoid duplicating information that the
environment already contains. For instance, it has been suggested to record a
name for environments, but e.g. virtual environments have the prompt recorded
in :file:`pyvenv.cfg`, so it does not need to be listed separately from the
environment where it may become stale.

This is also why the last line is the default environment: the expectation is
people will be adding the environment they want to use and not simply recording
an available environment. This all tries to make what is expected to be the
most common action the easiest action.

Having :file:`.venv` represent the last, and thus default, environment in a
:file:`.python-envs` file is for practical reasons. Tools that predate this
PEP may use the :file:`.venv` location, and so this is a
backwards-compatibility consideration. And if a user is using such a tool that
uses :file:`.venv`, then they likely already considered that virtual environment
the default.


Project Support for this PEP
============================

Speaking to various tool maintainers about this PEP:

.. note::
   Any tool without a link to an expression of (no) support gave that
   information privately, but with permission to state publicly.

- Supports

  1. VS Code


Backwards Compatibility
=======================

For the virtual environment location aspect of this PEP, there is no backwards
compatibility concern as :file:`.venv` is in this PEP specifically for
backwards compatibility.

As for :file:`.python-envs`, that file name is not known to be in use. The
biggest backwards compatibility concern is that a tool produces it and it is
not used as expected. After that is the file not being ignored by version
control upfront.


Security Implications
=====================

Not checking the contents of a potentially malicious :file:`.python-envs` file
and passing it to a shell process (e.g. ``subprocess.run(..., shell=True)``)
would be a serious security concern.


How to Teach This
=================

For new users, they can be told that ``python -m venv .venv`` creates a virtual
environment in :file:`.venv`, and that any other tool that creates a virtual
environment on their behalf can do the same.

For experienced users, they should be taught that tools may create a virtual
environment at :file:`.venv`. They should also be told there may be a
:file:`.python-envs` file which records the location of other environments with
the last environment listed considered the default. As well, they should be
taught that if both :file:`.venv` and :file:`.python-envs` exist in the same
directory then :file:`.venv` is implicitly the last, and thus default,
environment in :file:`.python-envs`.


Reference Implementation
========================

As this PEP proposes no code changes, there is no reference implementation to
speak of.


Rejected Ideas
==============

``.venv``
---------

Use a name other than ``.venv``
'''''''''''''''''''''''''''''''

Some people either don't like that ``.venv`` is hidden by some tools by
default thanks to the leading ``.``, or don't like ``venv`` as an
abbreviation. Since there doesn't seem to be a clear consensus on an
alternative, a different name doesn't fundamentally change any semantics,
existing tools seem to already support ``.venv``, and one can still use a
different name for an environment thanks to :file:`.python-envs` as proposed by
this PEP. Because the author of this PEP prefers the name,
``.venv`` was chosen. Discussing alternative names was viewed as bikeshedding.


``.python-envs``
----------------

A ``.venv`` redirect file
'''''''''''''''''''''''''

An earlier version of this PEP allowed for :file:`.venv` to act as a redirect
file to where the virtual environment is located. This was found to be too
restrictive compared to :file:`.python-envs` for a couple of reasons:

- It supported only a single environment
- It was restricted to only a virtual environment


Recording what tool manages an environment
''''''''''''''''''''''''''''''''''''''''''

It was suggested to have :file:`.python-envs` record what tool provided an
environment. The thinking was that there was the potential for orphaned
environments that still existed but were no longer valid for the project after
the user moved away from a tool or changed a configuration that wasn't obvious
to the user.

The decision was made, though, that this was outside of the scope of this PEP
and not worth complicating :file:`.python-envs` for. If a tool wanted to keep
track of what environments they created, that would be up to them to do in
their own way. As for orphaned environments that continued to exist, that would
only be a concern for the default environment as the user would need to choose
any other environment.


Using a more structured format
''''''''''''''''''''''''''''''

Using a more structured data format such as JSON for :file:`.python-envs` was
suggested. Typically it was in order to record details about the environment
directly in :file:`.python-envs`. But since that would be redundant data which
could be gathered from the environment itself, it was deemed not a reason to
make the file format more complicated. And the simplicity of the file format
has helped to keep the goal of the file specific and not have feature creep.


Storing the locations in pyproject.toml
'''''''''''''''''''''''''''''''''''''''

It was suggested to store the locations of the environment in
:file:`pyproject.toml`, but that was rejected as too rigid. Typically an
environment location is either a personal choice or a tool-specific one, not a
project one. As such, specifying the location statically didn't seem to make
enough sense to put into the PEP, especially as a project could include its own
:file:`.python-envs` file.


Using the first entry in .python-envs as the default environment
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

It's a subjective choice to have the default environment be at the end of a
:file:`.python-envs` file instead of at the start. The decision came down to
whether reading or writing should be preferred. The PEP chose the latter,
as its author believes that it is more important to make writing to a
:file:`.python-envs` file easier, as a person is more likely to do that than
to read it (the corollary is that, since a tool is more likely to read a
:file:`.python-envs` file via code, reading does not need to be optimized for a
person).

As well, the expectation is that reading even a :file:`.python-envs` file with
hundreds of locations will not visibly hurt performance in any way.


Leave .python-envs out of the PEP
'''''''''''''''''''''''''''''''''

Some have suggested leaving :file:`.python-envs` out of the PEP (or not having
this PEP at all). But during discussions around this PEP, the desire to have a
way to list the location of multiple environments no matter where they live
seemed strong enough to keep :file:`.python-envs` included.


Support a file name suffix for .python-envs
'''''''''''''''''''''''''''''''''''''''''''

There was a suggestion to allow for multiple :file:`.python-envs`-like files,
differing by a file suffix. The idea was to organize what an environment was
named/for. The idea could also be extended to have the files only contain a
single environment.

In the end it didn't seem worth the complexity. Environments would have their
own way to name themselves, giving some clue as to their contents. As well,
the person choosing which environment to use would not necessarily need such
labels. Finally, it could lead to so many files as to be annoying.


Deferred Ideas
==============

During the discussions for this PEP, it was suggested to be more bold and try
to come up with a way to standardize how workflow tools could communicate with
other tools. This would not only let workflow tools tell other tools where an
environment is, but also create environments, run commands in an environment,
etc. Conversations went far enough to
`vote on communication protocols <https://discuss.python.org/t/pep-832-virtual-environment-discovery/106998/117>`__
and `continue that discussion <https://discuss.python.org/t/various-ways-of-defining-how-to-call-a-workflow-tool-for-wsp/108033>`__.

In the end, though, it was decided this PEP could stand on its own without such
a tool-to-tool protocol which would be a massive endeavour. But the name of
"workflow service protocol" -- aka "WSP", which also means "whitespace" in many
parsing grammars -- was at least determined and generally liked.


Acknowledgements
================

Thanks to everyone who participated in an earlier discussion on this topic at
https://discuss.python.org/t/22922/. Thanks to Cary Hawkins of Hatch,
Randy Döring of Poetry, Frost Ming of PDM, Bernát Gábor of virtualenv & tox,
Vinay Sajip of venv, and Zanie Blue of uv for feedback on the initial draft of
this PEP.

Change History
==============

- 31-Jul-2026

  - Changed from :file:`.venv` redirect files to :file:`.python-envs`
  - Dropped all proposed changes to :mod:`venv`

- 23-Apr-2026

  - Add PyCharm and library-skills support
  - Have redirect files read up to the first newline
  - Clarify there is no opinion on having multiple virtual environments
  - Explicitly use the code editor example for the motivation
  - Have ``venv.executable()`` be configurable for the virtual environment
    name
  - Clarify symlinks are not to be treated in any special way
  - Move the Rationale after the Specification and simplify the latter by
    moving details to the former
  - Loosened things involving "MAY", "SHOULD", and "NOT" so tools are not
    required to do anything beyond how they interpret a redirect file


Copyright
=========

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.


.. _tox: https://tox.wiki/
