'\" t .\" Man page generated from reStructuredText .\" by the Docutils 0.23 manpage writer. . . .nr rst2man-indent-level 0 . .de1 rstReportMargin \\$1 \\n[an-margin] level \\n[rst2man-indent-level] level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] - \\n[rst2man-indent0] \\n[rst2man-indent1] \\n[rst2man-indent2] .. .de1 INDENT .\" .rstReportMargin pre: . RS \\$1 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] . nr rst2man-indent-level +1 .\" .rstReportMargin post: .. .de UNINDENT . RE .\" indent \\n[an-margin] .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] .nr rst2man-indent-level -1 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. .TH "VIRTUALENV" "1" "June 19, 2026" "21.5" "virtualenv" .SH NAME virtualenv \- virtualenv 21.5.1 [image: Latest version on PyPI] [image] \%[image: PyPI - Implementation] [image] [image: PyPI - Python Version] [image] [image: Documentation status] [image] \%[image: Discord] [image] \%[image: PyPI - Downloads] [image] \%[image: PyPI - License] [image] \%[image: Open issues] [image] \%[image: Open pull requests] [image] \%[image: Package popularity] [image] \% .sp \fBvirtualenv\fP is a tool to create isolated Python environments. Since Python 3.3, a subset of it has been integrated into the standard library under the \fBvenv\fP module. For how \fBvirtualenv\fP compares to the stdlib \fBvenv\fP module, see Explanation \%<>\&. .SH QUICK NAVIGATION .sp \fBTutorials\fP \- Learn by doing .INDENT 0.0 .IP \(bu 2 Getting started \%<> — Create your first virtual environment and learn the basic workflow .UNINDENT .sp \fBHow\-to guides\fP \- Solve specific problems .INDENT 0.0 .IP \(bu 2 Install virtualenv \%<> — Install virtualenv on your system .IP \(bu 2 Use virtualenv \%<> — Select Python versions, activate environments, configure defaults, and use from Python code .UNINDENT .sp \fBReference\fP \- Technical information .INDENT 0.0 .IP \(bu 2 Compatibility \%<> — Supported Python versions and operating systems .IP \(bu 2 Command line \%<> — Command line options and flags .IP \(bu 2 Python \%<> — Programmatic Python API reference .UNINDENT .sp \fBExplanation\fP \- Understand the concepts .INDENT 0.0 .IP \(bu 2 Explanation \%<> — How virtualenv works under the hood and why it exists .UNINDENT .sp \fBExtensions\fP .INDENT 0.0 .IP \(bu 2 Plugins \%<> — Extend virtualenv with custom creators, seeders, and activators .UNINDENT .SH RELATED PROJECTS .sp Several tools build on virtualenv to provide higher\-level workflows: .INDENT 0.0 .IP \(bu 2 virtualenvwrapper \% — Shell wrapper for creating and managing multiple virtualenvs .IP \(bu 2 pew \% — Python Env Wrapper, a set of commands to manage multiple virtual environments .IP \(bu 2 tox \% — Automate testing across multiple Python versions .IP \(bu 2 nox \% — Flexible test automation in Python .UNINDENT .SH EXTERNAL RESOURCES .sp Learn more about virtualenv from these community resources: .INDENT 0.0 .IP \(bu 2 Corey Schafer\(aqs virtualenv tutorial \% — Video walkthrough for beginners .IP \(bu 2 Bernat Gabor\(aqs status quo \% — Talk about the current state of Python packaging .IP \(bu 2 Carl Meyer\(aqs reverse\-engineering \% — Deep dive into how virtualenv works internally .UNINDENT .SS Getting started .sp This tutorial will teach you the basics of virtualenv through hands\-on practice. You\(aqll create your first virtual environment, install packages, and learn how to manage project dependencies. .SS Prerequisites .sp Before starting this tutorial, you need: .INDENT 0.0 .IP \(bu 2 Python 3.9 or later installed on your system. If you use a version manager like pyenv \%, mise \%, or asdf \%, virtualenv will automatically discover the Python version they manage. .IP \(bu 2 virtualenv installed (see Install virtualenv \%<>). .UNINDENT .SS Create your first virtual environment .sp Let\(aqs create a virtual environment called \fBmyproject\fP: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv myproject created virtual environment CPython3.13.2.final.0\-64 in 200ms creator CPython3Posix(dest=/home/user/myproject, clear=False, no_vcs_ignore=False, global=False) seeder FromAppData(download=False, pip=bundle, setuptools=bundle, via=copy, app_data_dir=/home/user/.cache/virtualenv) activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator .EE .UNINDENT .UNINDENT .sp This creates a new directory called \fBmyproject\fP containing a complete, isolated Python environment with its own copy of Python, pip, and other tools. .SS Activate the environment .sp To use your virtual environment, you can activate it. The activation command differs by platform: [Linux/macOS] .INDENT 0.0 .INDENT 3.5 .sp .EX $ source myproject/bin/activate .EE .UNINDENT .UNINDENT [Windows (PowerShell)] .INDENT 0.0 .INDENT 3.5 .sp .EX PS> .\emyproject\eScripts\eActivate.ps1 .EE .UNINDENT .UNINDENT [Windows (CMD)] .INDENT 0.0 .INDENT 3.5 .sp .EX C:\e> .\emyproject\eScripts\eactivate.bat .EE .UNINDENT .UNINDENT .sp After activation, your prompt changes to show the active environment: .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) $ .EE .UNINDENT .UNINDENT .sp You can verify that Python is now running from inside the virtual environment: [Linux/macOS] .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) $ which python /home/user/myproject/bin/python .EE .UNINDENT .UNINDENT [Windows (PowerShell)] .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) PS> where.exe python C:\eUsers\euser\emyproject\eScripts\epython.exe .EE .UNINDENT .UNINDENT [Windows (CMD)] .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) C:\e> where.exe python C:\eUsers\euser\emyproject\eScripts\epython.exe .EE .UNINDENT .UNINDENT .SS Install a package .sp With the environment activated, install a package using pip: .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) $ pip install requests Collecting requests Using cached requests\-2.32.3\-py3\-none\-any.whl (64 kB) Installing collected packages: requests Successfully installed requests\-2.32.3 .EE .UNINDENT .UNINDENT .sp Verify that the package is installed only inside your virtual environment: .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) $ python \-c \(dqimport requests; print(requests.__file__)\(dq /home/user/myproject/lib/python3.13/site\-packages/requests/__init__.py .EE .UNINDENT .UNINDENT .sp The path shows that \fBrequests\fP is installed in the virtual environment, not in your system Python. .SS Deactivate .sp When you\(aqre done working in the virtual environment, deactivate it: .INDENT 0.0 .INDENT 3.5 .sp .EX (myproject) $ deactivate $ .EE .UNINDENT .UNINDENT .sp The prompt returns to normal, and Python commands now use your system Python again. .SS Use without activation .sp Activation is a convenience, not a requirement. You can run any executable from the virtual environment directly by using its full path: [Linux/macOS] .INDENT 0.0 .INDENT 3.5 .sp .EX $ myproject/bin/python \-c \(dqimport sys; print(sys.prefix)\(dq /home/user/myproject $ myproject/bin/pip install httpx .EE .UNINDENT .UNINDENT [Windows (PowerShell)] .INDENT 0.0 .INDENT 3.5 .sp .EX PS> .\emyproject\eScripts\epython.exe \-c \(dqimport sys; print(sys.prefix)\(dq C:\eUsers\euser\emyproject PS> .\emyproject\eScripts\epip.exe install httpx .EE .UNINDENT .UNINDENT [Windows (CMD)] .INDENT 0.0 .INDENT 3.5 .sp .EX C:\e> .\emyproject\eScripts\epython.exe \-c \(dqimport sys; print(sys.prefix)\(dq C:\eUsers\euser\emyproject C:\e> .\emyproject\eScripts\epip.exe install httpx .EE .UNINDENT .UNINDENT .sp This is especially useful in scripts, CI pipelines, and automation where modifying the shell environment is unnecessary. .SS Set up a real project .sp Now let\(aqs apply what you\(aqve learned to a real project workflow: .INDENT 0.0 .INDENT 3.5 .sp .EX $ mkdir myapp && cd myapp $ virtualenv venv $ source venv/bin/activate # or use the appropriate command for your platform (venv) $ pip install flask requests (venv) $ pip freeze > requirements.txt .EE .UNINDENT .UNINDENT .sp The \fBrequirements.txt\fP file now contains your project\(aqs dependencies: .INDENT 0.0 .INDENT 3.5 .sp .EX blinker==1.9.0 certifi==2025.1.31 charset\-normalizer==3.4.1 click==8.1.8 flask==3.1.0 idna==3.10 itsdangerous==2.2.0 Jinja2==3.1.5 MarkupSafe==3.0.2 requests==2.32.3 urllib3==2.3.0 werkzeug==3.1.3 .EE .UNINDENT .UNINDENT .sp This file lets you recreate the exact environment later. Let\(aqs test this: .INDENT 0.0 .INDENT 3.5 .sp .EX (venv) $ deactivate $ rm \-rf venv $ virtualenv venv $ source venv/bin/activate (venv) $ pip install \-r requirements.txt .EE .UNINDENT .UNINDENT .sp All packages are reinstalled exactly as before. Here\(aqs the complete workflow: [graph].SS What you learned .sp In this tutorial, you learned how to: .INDENT 0.0 .IP \(bu 2 Create a virtual environment with \fBvirtualenv\fP\&. .IP \(bu 2 Activate and deactivate virtual environments on different platforms. .IP \(bu 2 Install packages in isolation from your system Python. .IP \(bu 2 Save project dependencies with \fBpip freeze\fP\&. .IP \(bu 2 Reproduce environments using \fBrequirements.txt\fP\&. .UNINDENT .SS Next steps .sp Now that you understand the basics, explore these topics: .INDENT 0.0 .IP \(bu 2 Use virtualenv \%<> for selecting specific Python versions, configuring defaults, and advanced usage patterns. .IP \(bu 2 Explanation \%<> for understanding how virtualenv works under the hood and how it compares to \fBvenv\fP\&. .IP \(bu 2 Command line \%<> for all available command line options and flags. .UNINDENT .SS Install virtualenv .sp virtualenv is a command\-line tool, so it should be installed in an isolated environment rather than into your system Python. Pick the method that fits your setup: .INDENT 0.0 .IP \(bu 2 uv \% \-\- fast, modern Python package manager. Use this if you already have \fBuv\fP or are starting fresh. .IP \(bu 2 pipx \% \-\- installs Python CLI tools in isolated environments. Use this if you already have \fBpipx\fP set up. .IP \(bu 2 pip \% \-\- the standard Python package installer. Use \fB\-\-user\fP to avoid modifying system packages. May not work on distributions with externally\-managed Python environments. .IP \(bu 2 zipapp \% \-\- a self\-contained executable requiring no installation. Use this in CI or environments where you cannot install packages. .UNINDENT [graph][uv] Install virtualenv as a uv tool \%: .INDENT 0.0 .INDENT 3.5 .sp .EX $ uv tool install virtualenv .EE .UNINDENT .UNINDENT .sp Install the development version: .INDENT 0.0 .INDENT 3.5 .sp .EX $ uv tool install git+https://github.com/pypa/virtualenv.git@main .EE .UNINDENT .UNINDENT [pipx] Install virtualenv using pipx \%: .INDENT 0.0 .INDENT 3.5 .sp .EX $ pipx install virtualenv .EE .UNINDENT .UNINDENT .sp Install the development version: .INDENT 0.0 .INDENT 3.5 .sp .EX $ pipx install git+https://github.com/pypa/virtualenv.git@main .EE .UNINDENT .UNINDENT [pip] Install virtualenv using pip \%: .INDENT 0.0 .INDENT 3.5 .sp .EX $ python \-m pip install \-\-user virtualenv .EE .UNINDENT .UNINDENT .sp Install the development version: .INDENT 0.0 .INDENT 3.5 .sp .EX $ python \-m pip install git+https://github.com/pypa/virtualenv.git@main .EE .UNINDENT .UNINDENT .sp \fBWarning:\fP .INDENT 0.0 .INDENT 3.5 Some Linux distributions use system\-managed Python environments. If you encounter errors about externally\-managed environments, use \fBuv tool\fP or \fBpipx\fP instead. .UNINDENT .UNINDENT [zipapp] Download the zipapp file and run it directly: .INDENT 0.0 .INDENT 3.5 .sp .EX $ python virtualenv.pyz \-\-help .EE .UNINDENT .UNINDENT .sp Download the latest version from \% or a specific version from \fBhttps://bootstrap.pypa.io/virtualenv/x.y/virtualenv.pyz\fP\&. .SS Verify installation .sp Check the installed version: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-version .EE .UNINDENT .UNINDENT .sp See Compatibility \%<> for supported Python versions. .SS Use virtualenv .SS Select a Python version .sp By default, virtualenv uses the same Python version it runs under. Override this with \fB\-\-python\fP or \fB\-p\fP\&. .SS Using version specifiers .sp Specify a Python version by name or version number: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-p python3.14 venv $ virtualenv \-p 3.10 venv $ virtualenv \-p pypy3 venv $ virtualenv \-p rustpython venv .EE .UNINDENT .UNINDENT .SS Using PEP 440 specifiers .sp Use PEP 440 \% version specifiers to match Python versions: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-python \(dq>=3.12\(dq venv $ virtualenv \-\-python \(dq~=3.11.0\(dq venv $ virtualenv \-\-python \(dqcpython>=3.10\(dq venv .EE .UNINDENT .UNINDENT .INDENT 0.0 .IP \(bu 2 \fB>=3.12\fP \-\- any Python 3.12 or later. .IP \(bu 2 \fB~=3.11.0\fP \-\- compatible release, equivalent to \fB>=3.11.0, <3.12.0\fP (any 3.11.x patch). .IP \(bu 2 \fBcpython>=3.10\fP \-\- restrict to CPython implementation, 3.10 or later. .UNINDENT .SS Using free\-threading Python .sp Create an environment with free\-threading Python \%: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-p 3.13t venv .EE .UNINDENT .UNINDENT .SS Targeting a specific CPU architecture .sp On machines that support multiple architectures — such as Apple Silicon (arm64 + x86_64 via Rosetta) or Windows on ARM — you can request a specific CPU architecture by appending it to the spec string: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-p cpython3.12\-64\-arm64 venv $ virtualenv \-p 3.11\-64\-x86_64 venv .EE .UNINDENT .UNINDENT .sp Cross\-platform aliases are normalized automatically, so \fBamd64\fP and \fBx86_64\fP are treated as equivalent, as are \fBaarch64\fP and \fBarm64\fP\&. If omitted, any architecture matches (preserving existing behavior). .SS Using absolute paths .sp Specify the full path to a Python interpreter: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-p /usr/bin/python3.9 venv .EE .UNINDENT .UNINDENT .SS Using \fB\-\-try\-first\-with\fP .sp Use \fB\-\-try\-first\-with\fP to provide a hint about which Python to check first. Unlike \fB\-\-python\fP, this is a hint rather than a rule. The interpreter at this path is checked first, but only used if it matches the \fB\-\-python\fP constraint. .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-python \(dq>=3.10\(dq \-\-try\-first\-with /usr/bin/python3.9 venv .EE .UNINDENT .UNINDENT .sp In this example, /usr/bin/python3.9 is checked first but rejected because it does not satisfy the >=3.10 constraint. .SS Using version managers (pyenv, mise, asdf) .sp virtualenv automatically resolves shims from pyenv \%, mise \%, and asdf \% to the real Python binary. Set the active Python version using any of the standard mechanisms and virtualenv will discover it: .INDENT 0.0 .INDENT 3.5 .sp .EX $ pyenv local 3.12.0 $ virtualenv venv # uses pyenv\(aqs 3.12.0, not the system Python $ PYENV_VERSION=3.11.0 virtualenv venv # uses 3.11.0 .EE .UNINDENT .UNINDENT .sp This also works with mise and asdf: .INDENT 0.0 .INDENT 3.5 .sp .EX $ mise use python@3.12 $ virtualenv venv .EE .UNINDENT .UNINDENT .sp No additional configuration is required. See Explanation \%<> for details on how shim resolution works. .SS Activate a virtual environment .sp Activate the environment to modify your shell\(aqs PATH and environment variables. [Bash/Zsh] .INDENT 0.0 .INDENT 3.5 .sp .EX $ source venv/bin/activate .EE .UNINDENT .UNINDENT [Fish] .INDENT 0.0 .INDENT 3.5 .sp .EX $ source venv/bin/activate.fish .EE .UNINDENT .UNINDENT [PowerShell] .INDENT 0.0 .INDENT 3.5 .sp .EX PS> .\evenv\eScripts\eActivate.ps1 .EE .UNINDENT .UNINDENT .sp \fBNote:\fP .INDENT 0.0 .INDENT 3.5 If you encounter an execution policy error, run \fBSet\-ExecutionPolicy RemoteSigned\fP to allow local scripts. .UNINDENT .UNINDENT [CMD] .INDENT 0.0 .INDENT 3.5 .sp .EX > .\evenv\eScripts\eactivate.bat .EE .UNINDENT .UNINDENT [Nushell] .INDENT 0.0 .INDENT 3.5 .sp .EX $ overlay use venv/bin/activate.nu .EE .UNINDENT .UNINDENT [Xonsh] .INDENT 0.0 .INDENT 3.5 .sp .EX @ source venv/bin/activate.xsh .EE .UNINDENT .UNINDENT .SS Deactivate the environment .sp Exit the virtual environment: .INDENT 0.0 .INDENT 3.5 .sp .EX $ deactivate .EE .UNINDENT .UNINDENT .SS Use without activation .sp Use the environment without activating it by calling executables with their full paths: .INDENT 0.0 .INDENT 3.5 .sp .EX $ venv/bin/python script.py $ venv/bin/pip install package .EE .UNINDENT .UNINDENT .SS Customize prompt .sp Set a custom prompt prefix: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-prompt myproject venv .EE .UNINDENT .UNINDENT .sp Disable the prompt modification by setting the \fBVIRTUAL_ENV_DISABLE_PROMPT\fP environment variable. .sp Access the prompt string via the \fBVIRTUAL_ENV_PROMPT\fP environment variable. .SS Programmatic activation .sp Activate the environment from within a running Python process using \fBactivate_this.py\fP\&. This modifies \fBsys.path\fP and environment variables in the current process so that subsequent imports resolve from the virtual environment. .INDENT 0.0 .INDENT 3.5 .sp .EX import runpy runpy.run_path(\(dqvenv/bin/activate_this.py\(dq) .EE .UNINDENT .UNINDENT .sp A common use case is web applications served by a system\-wide WSGI server (such as mod_wsgi or uWSGI) that need to load packages from a virtual environment: .INDENT 0.0 .INDENT 3.5 .sp .EX import runpy from pathlib import Path runpy.run_path(str(Path(\(dq/var/www/myapp/venv/bin/activate_this.py\(dq))) from myapp import create_app # noqa: E402 application = create_app() .EE .UNINDENT .UNINDENT .SS Configure defaults .sp Use a configuration file to set default options for virtualenv. .SS Configuration file location .sp The configuration file is named \fBvirtualenv.ini\fP and located in the platformdirs app config directory. Run \fBvirtualenv \-\-help\fP to see the exact location for your system. .sp Override the location with the \fBVIRTUALENV_CONFIG_FILE\fP environment variable. .SS Configuration format .sp Derive configuration keys from command\-line options by stripping leading \fB\-\fP and replacing remaining \fB\-\fP with \fB_\fP: .INDENT 0.0 .INDENT 3.5 .sp .EX [virtualenv] python = /opt/python\-3.14/bin/python .EE .UNINDENT .UNINDENT .SS Multi\-value options .sp Specify multiple values on separate lines: .INDENT 0.0 .INDENT 3.5 .sp .EX [virtualenv] extra_search_dir = /path/to/dists /path/to/other/dists .EE .UNINDENT .UNINDENT .SS Environment variables .sp Set options using environment variables with the \fBVIRTUALENV_\fP prefix and uppercase key names: .INDENT 0.0 .INDENT 3.5 .sp .EX $ export VIRTUALENV_PYTHON=/opt/python\-3.14/bin/python .EE .UNINDENT .UNINDENT .sp For multi\-value options, separate values with commas or newlines. .SS Override app\-data location .sp Set the \fBVIRTUALENV_OVERRIDE_APP_DATA\fP environment variable to override the default app\-data cache directory location. .SS Allow unverified HTTPS for periodic updates .sp The periodic update checks for newer seed wheels by fetching \fBhttps://pypi.org/pypi//json\fP\&. The request is made with full TLS verification and, if the verified request fails, virtualenv now skips the update rather than downgrading the connection. Set \fBVIRTUALENV_PERIODIC_UPDATE_INSECURE=1\fP to restore the previous behavior and retry with an unverified SSL context. This is an escape hatch for hosts with broken trust stores; do not set it on hosts you do not control. .SS Configuration priority .sp Options are resolved in this order (highest to lowest priority): [graph].SS Control seed packages .SS Upgrade embedded wheels .sp Update the embedded wheel files to the latest versions: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-upgrade\-embed\-wheels .EE .UNINDENT .UNINDENT .SS Provide custom wheels .sp Use custom wheel files from a local directory: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-extra\-search\-dir /path/to/wheels venv .EE .UNINDENT .UNINDENT .SS Download latest from PyPI .sp Download the latest versions of seed packages from PyPI: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-download venv .EE .UNINDENT .UNINDENT .SS Disable periodic updates .sp Disable automatic periodic updates of seed packages: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-no\-periodic\-update venv .EE .UNINDENT .UNINDENT .SS For distribution maintainers .sp Patch the \fBvirtualenv.seed.wheels.embed\fP module and set \fBPERIODIC_UPDATE_ON_BY_DEFAULT\fP to \fBFalse\fP to disable periodic updates by default. See Explanation \%<> for implementation details. .SS Use from Python code .sp Call virtualenv from Python code using the \fBcli_run\fP function: .INDENT 0.0 .INDENT 3.5 .sp .EX from virtualenv import cli_run cli_run([\(dqvenv\(dq]) .EE .UNINDENT .UNINDENT .sp Pass options as list elements: .INDENT 0.0 .INDENT 3.5 .sp .EX cli_run([\(dq\-p\(dq, \(dqpython3.14\(dq, \(dq\-\-without\-pip\(dq, \(dqmyenv\(dq]) .EE .UNINDENT .UNINDENT .sp Use the returned session object to access environment details: .INDENT 0.0 .INDENT 3.5 .sp .EX result = cli_run([\(dqvenv\(dq]) print(result.creator.dest) # path to created environment print(result.creator.exe) # path to python executable .EE .UNINDENT .UNINDENT .sp Use \fBsession_via_cli\fP to describe the environment without creating it: .INDENT 0.0 .INDENT 3.5 .sp .EX from virtualenv import session_via_cli session = session_via_cli([\(dqvenv\(dq]) # inspect session.creator, session.seeder, session.activators .EE .UNINDENT .UNINDENT .sp See Python \%<> for complete API documentation. .SS Compatibility .SS Supported Python implementations .sp \fBvirtualenv\fP works with the following Python interpreter implementations. Only the latest patch version of each minor version is fully supported; previous patch versions work on a best effort basis. .SS CPython .sp \fB3.14 >= python_version >= 3.9\fP .SS PyPy .sp \fB3.11 >= python_version >= 3.9\fP .SS GraalPy .sp \fB24.1\fP and later (Linux and macOS only). .SS RustPython .sp Experimental support (Linux, macOS, and Windows). RustPython \% implements Python 3.14. .SS Support policy .INDENT 0.0 .IP \(bu 2 \fBNew versions\fP are added close to their release date, typically during the beta phase. .IP \(bu 2 \fBOld versions\fP are dropped 18 months after CPython EOL \%, giving users plenty of time to migrate. .UNINDENT .SS Version support timeline .sp Major version support changes: .INDENT 0.0 .IP \(bu 2 \fB21.5.0\fP (2026\-06\-13): dropped support for running under and creating environments for Python 3.8 and earlier. .IP \(bu 2 \fB20.27.0\fP (2024\-10\-17): dropped support for running under Python 3.7 and earlier. .IP \(bu 2 \fB20.22.0\fP (2023\-04\-19): dropped support for creating environments for Python 3.6 and earlier. .IP \(bu 2 \fB20.18.0\fP (2023\-02\-06): dropped support for running under Python 3.6 and earlier. .UNINDENT .SS Supported operating systems .sp CPython is shipped in multiple forms, and each OS repackages it, often applying some customization. The platforms listed below are tested. Unlisted platforms may work but are not explicitly supported. If you encounter issues on unlisted platforms, please open a feature request. .SS Cross\-platform .sp These Python distributions work on Linux, macOS, and Windows: .INDENT 0.0 .IP \(bu 2 Installations from python.org \% .IP \(bu 2 python\-build\-standalone \% builds (used by uv \% and mise \%) .IP \(bu 2 Python versions managed by pyenv \%, mise \%, or asdf \% (shims are automatically resolved to the real binary) .UNINDENT .SS Linux .INDENT 0.0 .IP \(bu 2 Ubuntu 16.04 and later (both upstream and deadsnakes \% builds) .IP \(bu 2 Fedora .IP \(bu 2 RHEL and CentOS .IP \(bu 2 OpenSuse .IP \(bu 2 Arch Linux .UNINDENT .SS macOS .INDENT 0.0 .IP \(bu 2 Python versions installed via Homebrew \% (works, but not recommended \% \-\- Homebrew may upgrade or remove Python versions without warning, breaking existing virtual environments) .IP \(bu 2 Python 3 part of XCode (Python framework builds at \fB/Library/Frameworks/Python3.framework/\fP) .UNINDENT .sp \fBNote:\fP .INDENT 0.0 .INDENT 3.5 Framework builds do not support copy\-based virtual environments. Use symlink or hardlink creation methods instead. .UNINDENT .UNINDENT .SS Windows .INDENT 0.0 .IP \(bu 2 Windows Store \% Python 3.9 and later .UNINDENT .SS Command line .sp \fBvirtualenv\fP is primarily a command line application. All options have sensible defaults, and there is one required argument: the name or path of the virtual environment to create. .sp See Use virtualenv \%<> for how to select Python versions, configure defaults, and use environment variables. .SS Command line options .sp \fBvirtualenv [OPTIONS]\fP .TS box center; l|l|l. T{ \fBNamed Arguments\fP T} _ T{ \fB\-\-version\fP T} T{ \fB\(aq==SUPPRESS==\(aq\fP T} T{ display the version of the virtualenv package and its location, then exit T} _ T{ \fB\-\-with\-traceback\fP T} T{ \fBFalse\fP T} T{ on failure also display the stacktrace internals of virtualenv T} _ T{ \fB\-\-read\-only\-app\-data\fP T} T{ \fBFalse\fP T} T{ use app data folder in read\-only mode (write operations will fail with error) T} _ T{ \fB\-\-app\-data\fP T} T{ platform specific application data folder T} T{ a data folder used as cache by the virtualenv T} _ T{ \fB\-\-reset\-app\-data\fP T} T{ \fBFalse\fP T} T{ start with empty app data folder T} _ T{ \fB\-\-upgrade\-embed\-wheels\fP T} T{ \fBFalse\fP T} T{ trigger a manual update of the embedded wheels T} .TE .TS box center; l|l|l. T{ \fBverbosity\fP ⇒ verbosity = verbose \- quiet, default INFO, mapping => CRITICAL=0, ERROR=1, WARNING=2, INFO=3, DEBUG=4, NOTSET=5 T} _ T{ \fB\-v\fP, \fB\-\-verbose\fP T} T{ \fB2\fP T} T{ increase verbosity T} _ T{ \fB\-q\fP, \fB\-\-quiet\fP T} T{ \fB0\fP T} T{ decrease verbosity T} .TE .SS discovery .TS box center; l|l|l. T{ \fBcore\fP ⇒ options shared across all discovery T} _ T{ \fB\-\-discovery\fP T} T{ \fB\(aqbuiltin\(aq\fP T} T{ interpreter discovery method; choice of: \fBbuiltin\fP T} _ T{ \fB\-p\fP, \fB\-\-python\fP T} T{ the python executable virtualenv is installed into T} T{ interpreter based on what to create environment (path/identifier/version\-specifier) \- by default use the interpreter where the tool is installed \- first found wins. Version specifiers (e.g., >=3.12, ~=3.11.0, ==3.10) are also supported T} _ T{ \fB\-\-try\-first\-with\fP T} T{ \fB[]\fP T} T{ try first these interpreters before starting the discovery T} .TE .SS creator .TS box center; l|l|l. T{ \fBcore\fP ⇒ options shared across all creator T} _ T{ \fB\-\-creator\fP T} T{ \fBbuiltin\fP if exist, else \fBvenv\fP T} T{ create environment via; choice of: \fBcpython3\-mac\-brew\fP, \fBcpython3\-mac\-framework\fP, \fBcpython3\-posix\fP, \fBcpython3\-win\fP, \fBgraalpy\-posix\fP, \fBgraalpy\-win\fP, \fBpypy3\-posix\fP, \fBpypy3\-win\fP, \fBrustpython\-posix\fP, \fBrustpython\-win\fP, \fBvenv\fP T} _ T{ \fBdest\fP T} T{ T} T{ directory to create virtualenv at T} _ T{ \fB\-\-clear\fP T} T{ \fBFalse\fP T} T{ remove the destination directory if exist before starting (will overwrite files otherwise) T} _ T{ \fB\-\-no\-vcs\-ignore\fP T} T{ \fBFalse\fP T} T{ don\(aqt create VCS ignore directive in the destination directory T} _ T{ \fB\-\-system\-site\-packages\fP T} T{ \fBFalse\fP T} T{ give the virtual environment access to the system site\-packages dir T} _ T{ \fB\-\-symlinks\fP T} T{ \fBTrue\fP T} T{ try to use symlinks rather than copies, when symlinks are not the default for the platform T} _ T{ \fB\-\-copies\fP, \fB\-\-always\-copy\fP T} T{ \fBFalse\fP T} T{ try to use copies rather than symlinks, even when symlinks are the default for the platform T} .TE .SS seeder .TS box center; l|l|l. T{ \fBcore\fP ⇒ options shared across all seeder T} _ T{ \fB\-\-seeder\fP T} T{ \fB\(aqapp\-data\(aq\fP T} T{ seed packages install method; choice of: \fBapp\-data\fP, \fBpip\fP T} _ T{ \fB\-\-no\-seed\fP, \fB\-\-without\-pip\fP T} T{ \fBFalse\fP T} T{ do not install seed packages T} _ T{ \fB\-\-no\-download\fP, \fB\-\-never\-download\fP T} T{ \fBTrue\fP T} T{ pass to disable download of the latest pip/setuptools/wheel from PyPI T} _ T{ \fB\-\-download\fP T} T{ \fBFalse\fP T} T{ pass to enable download of the latest pip/setuptools/wheel from PyPI T} _ T{ \fB\-\-extra\-search\-dir\fP T} T{ \fB[]\fP T} T{ a path containing wheels to extend the internal wheel list (can be set 1+ times) T} _ T{ \fB\-\-pip\fP T} T{ \fB\(aqbundle\(aq\fP T} T{ version of pip to install as seed: embed, bundle, none or exact version T} _ T{ \fB\-\-setuptools\fP T} T{ \fB\(aqnone\(aq\fP T} T{ version of setuptools to install as seed: embed, bundle, none or exact version T} _ T{ \fB\-\-no\-pip\fP T} T{ \fBFalse\fP T} T{ do not install pip T} _ T{ \fB\-\-no\-setuptools\fP T} T{ \fBFalse\fP T} T{ do not install setuptools T} _ T{ \fB\-\-no\-periodic\-update\fP T} T{ \fBFalse\fP T} T{ disable the periodic (once every 14 days) update of the embedded wheels T} .TE .TS box center; l|l|l. T{ \fBapp\-data\fP ⇒ options specific to seeder app\-data T} _ T{ \fB\-\-symlink\-app\-data\fP T} T{ \fBFalse\fP T} T{ symlink the python packages from the app\-data folder (requires seed pip>=19.3) T} .TE .SS activators .TS box center; l|l|l. T{ \fBcore\fP ⇒ options shared across all activators T} _ T{ \fB\-\-activators\fP T} T{ comma separated list of activators supported T} T{ activators to generate \- default is all supported; choice of: \fBxonsh\fP, \fBbash\fP, \fBbatch\fP, \fBcshell\fP, \fBfish\fP, \fBnushell\fP, \fBpowershell\fP, \fBpython\fP T} _ T{ \fB\-\-prompt\fP T} T{ T} T{ provides an alternative prompt prefix for this environment (value of . means name of the current working directory) T} .TE .SS Python .sp The primary interface to \fBvirtualenv\fP is the command line application. However, it can also be used programmatically via the \fBvirtualenv.cli_run\fP function and the \fBSession\fP class. .sp See Use virtualenv \%<> for usage examples. .SS virtualenv module .INDENT 0.0 .TP .B virtualenv.cli_run(args, options=None, setup_logging=True, env=None) Create a virtual environment given some command line interface arguments. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBargs\fP (\fBlist\fP \%[\fBstr\fP \%]) \-\- the command line arguments .IP \(bu 2 \fBoptions\fP (\fBVirtualEnvOptions\fP | \fBNone\fP \%) \-\- passing in a \fBVirtualEnvOptions\fP object allows return of the parsed options .IP \(bu 2 \fBsetup_logging\fP (\fBbool\fP \%) \-\- \fBTrue\fP if setup logging handlers, \fBFalse\fP to use handlers already registered .IP \(bu 2 \fBenv\fP (\fBMutableMapping\fP \%[\fBstr\fP \%, \fBstr\fP \%] | \fBNone\fP \%) \-\- environment variables to use .UNINDENT .TP .B Return type \fBSession\fP .TP .B Returns the session object of the creation (its structure for now is experimental and might change on short notice) .UNINDENT .UNINDENT .INDENT 0.0 .TP .B virtualenv.session_via_cli(args, options=None, setup_logging=True, env=None) Create a virtualenv session (same as cli_run, but this does not perform the creation). Use this if you just want to query what the virtual environment would look like, but not actually create it. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBargs\fP (\fBlist\fP \%[\fBstr\fP \%]) \-\- the command line arguments .IP \(bu 2 \fBoptions\fP (\fBVirtualEnvOptions\fP | \fBNone\fP \%) \-\- passing in a \fBVirtualEnvOptions\fP object allows return of the parsed options .IP \(bu 2 \fBsetup_logging\fP (\fBbool\fP \%) \-\- \fBTrue\fP if setup logging handlers, \fBFalse\fP to use handlers already registered .IP \(bu 2 \fBenv\fP (\fBMutableMapping\fP \%[\fBstr\fP \%, \fBstr\fP \%] | \fBNone\fP \%) \-\- environment variables to use .UNINDENT .TP .B Return type \fBSession\fP .TP .B Returns the session object of the creation (its structure for now is experimental and might change on short notice) .UNINDENT .UNINDENT .SS Session class .sp The \fBSession\fP class represents a virtualenv creation session and provides access to the created environment\(aqs properties. .INDENT 0.0 .TP .B class virtualenv.run.session.Session(verbosity, app_data, interpreter, creator, seeder, activators) Represents a virtual environment creation session. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBverbosity\fP (\fBint\fP \%) .IP \(bu 2 \fBapp_data\fP (\fBAppData\fP \%<#\:virtualenv\:.app_data\:.base\:.AppData>) .IP \(bu 2 \fBinterpreter\fP (\fBPythonInfo\fP \%<#\:virtualenv\:.discovery\:.py_info\:.PythonInfo>) .IP \(bu 2 \fBcreator\fP (\fBCreator\fP \%<#\:virtualenv\:.create\:.creator\:.Creator>) .IP \(bu 2 \fBseeder\fP (\fBSeeder\fP \%<#\:virtualenv\:.seed\:.seeder\:.Seeder>) .IP \(bu 2 \fBactivators\fP (\fBlist\fP \%[\fBActivator\fP \%<#\:virtualenv\:.activation\:.activator\:.Activator>]) .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property verbosity: int \% The verbosity of the run. .UNINDENT .INDENT 7.0 .TP .B property interpreter: PythonInfo \%<#\:virtualenv\:.discovery\:.py_info\:.PythonInfo> Create a virtual environment based on this reference interpreter. .UNINDENT .INDENT 7.0 .TP .B property creator: Creator \%<#\:virtualenv\:.create\:.creator\:.Creator> The creator used to build the virtual environment (must be compatible with the interpreter). .UNINDENT .INDENT 7.0 .TP .B property seeder: Seeder \%<#\:virtualenv\:.seed\:.seeder\:.Seeder> The mechanism used to provide the seed packages (pip, setuptools, wheel). .UNINDENT .INDENT 7.0 .TP .B property activators: list \%[Activator \%<#\:virtualenv\:.activation\:.activator\:.Activator>] Activators used to generate activations scripts. .UNINDENT .UNINDENT .SS VirtualEnvOptions .sp Options namespace passed to plugin constructors, populated from the CLI, environment variables, and configuration files. .INDENT 0.0 .TP .B class virtualenv.config.cli.parser.VirtualEnvOptions(**kwargs) .INDENT 7.0 .TP .B Parameters \fBkwargs\fP (\fBAny\fP \%) .UNINDENT .INDENT 7.0 .TP .B set_src(key, value, src) Set an option value and record where it came from. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkey\fP (\fBstr\fP \%) \-\- the option name .IP \(bu 2 \fBvalue\fP (\fBAny\fP \%) \-\- the option value .IP \(bu 2 \fBsrc\fP (\fBstr\fP \%) \-\- the source of the value (e.g. \fB\(dqcli\(dq\fP, \fB\(dqenv var\(dq\fP, \fB\(dqdefault\(dq\fP) .UNINDENT .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B get_source(key) Return the source that provided a given option value. .INDENT 7.0 .TP .B Parameters \fBkey\fP (\fBstr\fP \%) \-\- the option name .TP .B Return type \fBstr\fP \% | \fBNone\fP \% .TP .B Returns the source string (e.g. \fB\(dqcli\(dq\fP, \fB\(dqenv var\(dq\fP, \fB\(dqdefault\(dq\fP), or \fBNone\fP if not tracked .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property verbosity: int \% | None \% The verbosity level, computed as \fBverbose \- quiet\fP, clamped to zero. .INDENT 7.0 .TP .B Returns the verbosity level, or \fBNone\fP if neither \fB\-\-verbose\fP nor \fB\-\-quiet\fP has been parsed yet .UNINDENT .UNINDENT .UNINDENT .SS Explanation .sp This page explains the design decisions and concepts behind virtualenv. It focuses on understanding why things work the way they do. .SS virtualenv vs venv vs uv .sp Since Python 3.3, the standard library includes the \fBvenv\fP module, which provides basic virtual environment creation following PEP 405 \%\&. uv \% is a newer, Rust\-based tool that also creates virtual environments via \fBuv venv\fP\&. .sp virtualenv occupies a middle ground: faster and more featureful than \fBvenv\fP, while remaining a pure Python solution with a plugin system for extensibility. .TS box center; l|l|l|l. T{ T} T{ \fBvenv\fP T} T{ \fBvirtualenv\fP T} T{ uv \% T} _ T{ Performance T} T{ Slowest (60s+); spawns pip \% as a subprocess to seed. T} T{ Fast; caches pre\-built install images, subsequent creation < 1 second. T} T{ Fastest; Rust implementation, milliseconds. Does not seed pip/setuptools by default. T} _ T{ Extensibility T} T{ No plugin system. T} T{ Plugin system for discovery, creation, seeding, and activation. T} T{ No plugin system. T} _ T{ Cross\-version T} T{ Only the Python version it runs under. T} T{ Any installed Python via auto\-discovery (registry, uv\-managed, PATH). T} T{ Any installed or uv\-managed Python. T} _ T{ Upgradeability T} T{ Tied to Python releases. T} T{ Independent via PyPI \%\&. T} T{ Independent via its own release cycle. T} _ T{ Programmatic API T} T{ Basic \fBcreate()\fP function only. T} T{ Full Python API; can describe environments without creating them. Used by tox \%, poetry \%, pipx \%, etc. T} T{ Command line only. T} _ T{ Type annotations T} T{ No \fBpy.typed\fP marker; limited annotations. T} T{ Fully typed with \fBPEP 561\fP \% \fBpy.typed\fP marker; checked by ty \%\&. T} T{ Not applicable (Rust binary). T} _ T{ Best for T} T{ Zero dependencies, basic needs. T} T{ Plugin extensibility, programmatic API, tool compatibility (tox \%, virtualenvwrapper \%). T} T{ Maximum speed, already using \fBuv\fP for package management. T} .TE [graph].SS Supported Python versions .sp virtualenv distinguishes between the \fBhost\fP interpreter (the Python running virtualenv itself) and the \fBtarget\fP interpreter (the Python for which the virtual environment is created). When \fB\-\-python\fP is not specified, the host and target are the same interpreter. .SS Host interpreter .sp virtualenv requires CPython or PyPy \fB3.9 or later\fP as the host interpreter. This is enforced by \fBrequires\-python >= 3.9\fP in the package metadata. .SS Target interpreter .sp The target interpreter can differ from the host. virtualenv can create virtual environments for any Python interpreter it can discover on the system, provided a matching creator exists. .TS box center; l|l|l. T{ Implementation T} T{ Platforms T} T{ Notes T} _ T{ CPython T} T{ Linux, macOS, Windows T} T{ 3.9+. Free\-threaded builds supported on 3.13+. Includes macOS framework, Homebrew, Microsoft Store, and Windows debug build support. T} _ T{ PyPy T} T{ Linux, macOS, Windows T} T{ 3.9+. T} _ T{ GraalPy T} T{ Linux, macOS, Windows T} T{ 24.1+. Minimal test coverage, marked experimental. T} _ T{ RustPython T} T{ Linux, macOS, Windows T} T{ Minimal test coverage, marked experimental. T} .TE .sp Seed packages (\fBpip\fP, \fBsetuptools\fP) are bundled for CPython 3.9 through 3.16. A target newer than the highest bundled version reuses the newest bundle. A target older than the oldest bundled version has no compatible bundled wheel, so the bundled seeders refuse it before the environment is created; pass \fB\-\-no\-seed\fP for an empty environment, or select a seeder that ships wheels for that version. .SS How virtualenv works .sp Python packaging often faces a fundamental problem: different applications require different versions of the same library. If Application A needs \fBrequests==2.25.1\fP but Application B needs \fBrequests==2.28.0\fP, installing both into the global site\-packages directory creates a conflict. Only one version can exist in a given location. .sp virtualenv solves this by creating isolated Python environments. Each environment has its own installation directories and can maintain its own set of installed packages, independent of other environments and the system Python. .sp virtualenv operates in two distinct phases: [graph].INDENT 0.0 .TP \fBPhase 1: Discover a Python interpreter\fP virtualenv first identifies which Python interpreter to use as the template for the virtual environment. By default, it uses the same Python version that virtualenv itself is running on. You can override this with the \fB\-\-python\fP flag to specify a different interpreter. .TP \fBPhase 2: Create the virtual environment\fP Once the target interpreter is identified, virtualenv creates the environment in four steps: .INDENT 7.0 .IP 1. 3 Create a Python executable matching the target interpreter .IP 2. 3 Install seed packages (pip, setuptools, wheel) to enable package installation .IP 3. 3 Install activation scripts for various shells .IP 4. 3 Create VCS ignore files (currently Git\(aqs \fB\&.gitignore\fP, skip with \fB\-\-no\-vcs\-ignore\fP) .UNINDENT .UNINDENT .sp An important design principle: virtual environments are not self\-contained. A complete Python installation consists of thousands of files, and copying all of them into every virtual environment would be wasteful. Instead, virtual environments are lightweight shells that borrow most content from the system Python. They contain only what\(aqs needed to redirect Python\(aqs behavior. .sp This design has two implications: .INDENT 0.0 .IP \(bu 2 Environment creation is fast because only a small number of files need to be created. .IP \(bu 2 Upgrading the system Python might affect existing virtual environments, since they reference the system Python\(aqs standard library and binary extensions. .UNINDENT .sp The Python executable in a virtual environment is effectively isolated from the one used to create it, but the supporting files are shared. .sp \fBWarning:\fP .INDENT 0.0 .INDENT 3.5 If you upgrade your system Python, existing virtual environments will still report the old version (the version number is embedded in the Python executable itself), but they will use the new version\(aqs standard library and binary extensions. This normally works without issues, but be aware that the environment is effectively running a hybrid of old and new Python versions. .UNINDENT .UNINDENT .SS Python discovery .sp Before creating a virtual environment, virtualenv must locate a Python interpreter. The interpreter determines the virtual environment\(aqs Python version, implementation (CPython, PyPy, etc.), and architecture (32\-bit or 64\-bit). .sp The \fB\-\-python\fP flag accepts several specifier formats: .INDENT 0.0 .TP \fBPath specifier\fP An absolute or relative path to a Python executable, such as \fB/usr/bin/python3.14\fP or \fB\&./python\fP\&. .TP \fBVersion specifier\fP A string following the format \fB{implementation}{version}{architecture}{machine}\fP where: .INDENT 7.0 .IP \(bu 2 Implementation is alphabetic characters (\fBpython\fP means any implementation; if omitted, defaults to \fBpython\fP). .IP \(bu 2 Version is dot\-separated numbers, optionally followed by \fBt\fP for free\-threading builds. .IP \(bu 2 Architecture is \fB\-64\fP or \fB\-32\fP (if omitted, means any architecture). .IP \(bu 2 Machine is the CPU instruction set architecture, e.g. \fB\-arm64\fP, \fB\-x86_64\fP, \fB\-aarch64\fP (if omitted, means any machine). Cross\-platform aliases are normalized automatically (\fBamd64\fP ↔ \fBx86_64\fP, \fBaarch64\fP ↔ \fBarm64\fP). .UNINDENT .sp Examples: .INDENT 7.0 .IP \(bu 2 \fBpython3.14.1\fP \- Any Python implementation with version 3.14.1 .IP \(bu 2 \fB3\fP \- Any Python implementation with major version 3 .IP \(bu 2 \fB3.13t\fP \- Any Python implementation version 3.13 with free\-threading enabled .IP \(bu 2 \fBcpython3\fP \- CPython implementation with major version 3 .IP \(bu 2 \fBpypy2\fP \- PyPy implementation with major version 2 .IP \(bu 2 \fBcpython3.12\-64\-arm64\fP \- CPython 3.12, 64\-bit, ARM64 architecture .IP \(bu 2 \fB3.11\-64\-x86_64\fP \- Any implementation, version 3.11, 64\-bit, x86_64 architecture .IP \(bu 2 \fBrustpython\fP \- RustPython implementation .UNINDENT .TP \fBPEP 440 version specifier\fP Version constraints using PEP 440 operators: .INDENT 7.0 .IP \(bu 2 \fB>=3.12\fP \- Any Python 3.12 or later .IP \(bu 2 \fB~=3.11.0\fP \- Compatible with Python 3.11.0 .IP \(bu 2 \fBcpython>=3.10\fP \- CPython 3.10 or later .UNINDENT .UNINDENT .sp When you provide a specifier, virtualenv searches for matching interpreters using this strategy: [graph].INDENT 0.0 .IP 1. 3 \fBWindows Registry\fP (Windows only): Check registered Python installations per PEP 514 \%\&. .IP 2. 3 \fBuv\-managed installations\fP: Check the \fBUV_PYTHON_INSTALL_DIR\fP environment variable or platform\-specific uv Python directories for managed Python installations. .IP 3. 3 \fBPATH search\fP: Search for executables on the \fBPATH\fP environment variable with names matching the specification. .UNINDENT .SS Version manager shim resolution .sp Version managers like pyenv \%, mise \%, and asdf \% place lightweight shim scripts on \fBPATH\fP that delegate to the real Python binary. When virtualenv discovers a Python interpreter by running it as a subprocess, shims may resolve to the wrong Python version (typically the system Python) because the shim\(aqs resolution logic depends on shell environment state that doesn\(aqt fully propagate to child processes. .sp virtualenv detects shims by checking whether the candidate executable lives in a known shim directory (\fB$PYENV_ROOT/shims\fP, \fB$MISE_DATA_DIR/shims\fP, or \fB$ASDF_DATA_DIR/shims\fP). When a shim is detected, virtualenv bypasses it and locates the real binary directly under the version manager\(aqs \fBversions\fP directory, using the active version from: .INDENT 0.0 .IP 1. 3 The \fBPYENV_VERSION\fP environment variable (colon\-separated for multiple versions). .IP 2. 3 A \fB\&.python\-version\fP file in the current directory or any parent directory. .IP 3. 3 The global version file at \fB$PYENV_ROOT/version\fP\&. .UNINDENT .sp This convention is shared across pyenv, mise, and asdf, so the same resolution logic works for all three. .sp \fBWarning:\fP .INDENT 0.0 .INDENT 3.5 Virtual environments typically reference the system Python\(aqs standard library. If you upgrade the system Python, the virtual environment will report the old version (embedded in its Python executable) but will actually use the new version\(aqs standard library content. This can cause confusion when debugging version\-specific behavior. .sp If you use a virtual environment\(aqs Python as the target for creating another virtual environment, virtualenv will detect the system Python version and create an environment matching the actual (upgraded) version, not the version reported by the virtual environment. .UNINDENT .UNINDENT .SS Creators .sp Creators are responsible for constructing the virtual environment structure. virtualenv supports two types of creators: .INDENT 0.0 .TP \fBvenv creator\fP This creator delegates the entire creation process to the standard library\(aqs \fBvenv\fP module, following PEP 405 \%\&. The venv creator requires spawning a subprocess to invoke the venv module, unless virtualenv is installed in the system Python. .sp The subprocess overhead can be significant, especially on Windows where process creation is expensive. .TP \fBbuiltin creator\fP This creator means virtualenv performs the creation itself by knowing exactly which files to create and which system files to reference. The builtin creator is actually a family of specialized creators for different combinations of Python implementation (CPython, PyPy, GraalPy, RustPython) and platform (Windows, POSIX). The name \fBbuiltin\fP is an alias that selects the first available builtin creator for the target environment. .sp Because builtin creators don\(aqt require subprocess invocation, they\(aqre generally faster than the venv creator. .UNINDENT [graph] .sp virtualenv defaults to using the builtin creator if one is available for the target environment, falling back to the venv creator otherwise. .SS Seeders .sp After creating the virtual environment structure, virtualenv installs seed packages that enable package management within the environment. The seed packages are: .INDENT 0.0 .IP \(bu 2 \fBpip\fP \- The package installer for Python (always installed). .IP \(bu 2 \fBsetuptools\fP \- Package development and installation library (disabled by default on Python 3.12+). .UNINDENT .sp virtualenv supports two seeding methods with dramatically different performance characteristics: .INDENT 0.0 .TP \fBpip seeder\fP This method uses the bundled pip wheel to install seed packages by spawning a child pip process. The subprocess performs a full installation, including unpacking wheels and generating metadata. This method is reliable but slow, typically consuming 98% of the total virtual environment creation time. .TP \fBapp\-data seeder\fP This method creates reusable install images in a user application data directory. The first time you create an environment with specific seed package versions, the app\-data seeder builds complete install images and stores them in the cache. Subsequent environment creations simply link or copy these pre\-built images into the virtual environment\(aqs \fBsite\-packages\fP directory. .sp Performance comparison for creating virtual environments: [graph] .sp On platforms that support symlinks efficiently (Linux, macOS), the app\-data seeder provides nearly instant seeding. .sp You can override the cache location using the \fBVIRTUALENV_OVERRIDE_APP_DATA\fP environment variable. .UNINDENT .SS Wheel acquisition .sp Both seeding methods require wheel files for the seed packages. virtualenv acquires wheels using a priority system: [graph].INDENT 0.0 .TP \fBEmbedded wheels\fP virtualenv ships with a set of wheels bundled directly into the package. These are tested with the virtualenv release and provide a baseline set of seed packages. Different Python versions require different package versions, so virtualenv bundles multiple wheels to support its wide Python version range. .TP \fBUpgraded embedded wheels\fP Users can manually upgrade the embedded wheels by running virtualenv with the \fB\-\-upgrade\-embed\-wheels\fP flag. This fetches newer versions of seed packages from PyPI and stores them in the user application data directory. Subsequent virtualenv invocations will use these upgraded wheels instead of the embedded ones. .sp virtualenv can also perform periodic automatic upgrades (see below). .TP \fBExtra search directories\fP Users can specify additional directories containing wheels using the \fB\-\-extra\-search\-dir\fP flag. This is useful in air\-gapped environments or when using custom package builds. .TP \fBPyPI download\fP If no suitable wheel is found in the above locations, or if the \fB\-\-download\fP flag is set, virtualenv will use pip to download the latest compatible version from PyPI. .UNINDENT .SS Periodic update mechanism .sp To keep the seed packages reasonably current without requiring users to manually upgrade virtualenv or run \fB\-\-upgrade\-embed\-wheels\fP, virtualenv implements a periodic automatic update system: [graph] .sp The 28\-day waiting period protects users from automatically adopting newly released packages that might contain bugs. The 1\-hour delay after download ensures continuous integration systems don\(aqt start using different package versions mid\-run, which could cause confusing test failures. .sp You can disable the periodic update mechanism with the \fB\-\-no\-periodic\-update\fP flag. .SS Distribution maintainer patching .sp Operating system distributions and package managers sometimes need to customize which seed package versions virtualenv uses. They want to align virtualenv\(aqs bundled packages with system package versions. .sp Distributions can patch the \fBvirtualenv.seed.wheels.embed\fP module, replacing the \fBget_embed_wheel\fP function with their own implementation that returns distribution\-provided wheels. If they want to use virtualenv\(aqs test suite for validation, they should also provide the \fBBUNDLE_FOLDER\fP, \fBBUNDLE_SUPPORT\fP, \fBMIN\fP, \fBMAX\fP, and \fBOLDEST_SUPPORTED\fP variables. \fBOLDEST_SUPPORTED\fP (the parsed form of \fBMIN\fP) sets the floor below which the bundled seeders refuse to seed, so a distribution that bundles wheels for an older Python must lower it to match. .sp Distributions should also consider patching \fBvirtualenv.seed.embed.base_embed.PERIODIC_UPDATE_ON_BY_DEFAULT\fP to \fBFalse\fP, allowing the system package manager to control seed package updates rather than virtualenv\(aqs periodic update mechanism. Users can still manually request upgrades via \fB\-\-upgrade\-embed\-wheels\fP, but automatic updates won\(aqt interfere with system\-managed packages. .SS Activators .sp Activation scripts modify the current shell environment to prioritize the virtual environment\(aqs executables. This is purely a convenience mechanism \- you can always use absolute paths to virtual environment executables without activating. .sp What activation does: [graph].INDENT 0.0 .TP \fBPATH modification\fP The activation script prepends the virtual environment\(aqs \fBbin\fP directory (\fBScripts\fP on Windows) to the \fBPATH\fP environment variable. This ensures that when you run \fBpython\fP, \fBpip\fP, or other executables, the shell finds the virtual environment\(aqs versions first. .TP \fBEnvironment variables\fP Activation sets several environment variables: .INDENT 7.0 .IP \(bu 2 \fBVIRTUAL_ENV\fP \- Absolute path to the virtual environment directory. .IP \(bu 2 \fBVIRTUAL_ENV_PROMPT\fP \- The prompt prefix (the environment name or custom value from \fB\-\-prompt\fP). .IP \(bu 2 \fBPKG_CONFIG_PATH\fP \- Modified to include the virtual environment\(aqs \fBlib/pkgconfig\fP directory. .UNINDENT .TP \fBPrompt modification\fP By default, activation prepends the environment name to your shell prompt, typically shown as \fB(venv)\fP before the regular prompt. This visual indicator helps you remember which environment is active. You can customize this with the \fB\-\-prompt\fP flag when creating the environment, or disable it entirely by setting the \fBVIRTUAL_ENV_DISABLE_PROMPT\fP environment variable. .TP \fBDeactivation\fP Activation scripts also provide a \fBdeactivate\fP command that reverses the changes, restoring your original PATH and removing the environment variables and prompt modifications. .UNINDENT .sp virtualenv provides activation scripts for multiple shells: .INDENT 0.0 .IP \(bu 2 Bash \% (\fBactivate\fP) .IP \(bu 2 Fish \% (\fBactivate.fish\fP) .IP \(bu 2 Csh/Tcsh \% (\fBactivate.csh\fP) .IP \(bu 2 PowerShell \% (\fBactivate.ps1\fP) .IP \(bu 2 Windows Batch \% (\fBactivate.bat\fP) .IP \(bu 2 Nushell \% (\fBactivate.nu\fP) .IP \(bu 2 Python (\fBactivate_this.py\fP) \-\- for programmatic activation from within a running Python process, see Programmatic activation \%<#\:programmatic-activation> .UNINDENT .sp \fBNote:\fP .INDENT 0.0 .INDENT 3.5 On Windows 7 and later, PowerShell\(aqs default execution policy is \fBRestricted\fP, which prevents running the \fBactivate.ps1\fP script. You can allow locally\-generated scripts to run by changing the execution policy: .INDENT 0.0 .INDENT 3.5 .sp .EX Set\-ExecutionPolicy RemoteSigned .EE .UNINDENT .UNINDENT .sp Since virtualenv generates \fBactivate.ps1\fP locally for each environment, PowerShell considers it a local script rather than a remote one and allows execution under the \fBRemoteSigned\fP policy. .UNINDENT .UNINDENT .sp Remember: activation is optional. The following commands are equivalent: .INDENT 0.0 .INDENT 3.5 .sp .EX # With activation source venv/bin/activate python script.py deactivate # Without activation venv/bin/python script.py .EE .UNINDENT .UNINDENT .sp For a deeper dive into how activation works under the hood, see Allison Kaptur\(aqs blog post There\(aqs no magic: virtualenv edition \%, which explains how virtualenv uses \fBPATH\fP and \fBPYTHONHOME\fP to isolate virtual environments. .SS See also .INDENT 0.0 .IP \(bu 2 Use virtualenv \%<> \- Practical guides for common virtualenv tasks. .IP \(bu 2 Command line \%<> \- Complete CLI reference documentation. .UNINDENT .SS Plugins .sp virtualenv can be extended via plugins using Python entry points. Plugins are automatically discovered from the Python environment where virtualenv is installed, allowing you to customize how virtual environments are created, seeded, and activated. .SS Extension points .sp virtualenv provides four extension points through entry point groups: .INDENT 0.0 .TP .B \fBvirtualenv.discovery\fP Python interpreter discovery plugins. These plugins locate and identify Python interpreters that will be used as the base for creating virtual environments. .TP .B \fBvirtualenv.create\fP Virtual environment creator plugins. These plugins handle the actual creation of the virtual environment structure, including copying or symlinking the Python interpreter and standard library. .TP .B \fBvirtualenv.seed\fP Seed package installer plugins. These plugins install initial packages (like pip, setuptools, wheel) into newly created virtual environments. .TP .B \fBvirtualenv.activate\fP Shell activation script plugins. These plugins generate shell\-specific activation scripts that modify the environment to use the virtual environment. .UNINDENT .sp All extension points follow a common pattern: virtualenv discovers registered entry points, builds CLI options from them, and executes the selected implementations during environment creation. .SS Your first plugin .sp This tutorial walks through creating a simple discovery plugin that locates Python interpreters managed by pyenv. .SS Create the package structure .sp Set up a new Python package with the following structure: .INDENT 0.0 .INDENT 3.5 .sp .EX virtualenv\-pyenv/ ├── pyproject.toml └── src/ └── virtualenv_pyenv/ └── __init__.py .EE .UNINDENT .UNINDENT .SS Configure the entry point .sp In \fBpyproject.toml\fP, declare your plugin as an entry point under the \fBvirtualenv.discovery\fP group: .INDENT 0.0 .INDENT 3.5 .sp .EX [project] name = \(dqvirtualenv\-pyenv\(dq version = \(dq0.1.0\(dq dependencies = [\(dqvirtualenv>=20\(dq] [project.entry\-points.\(dqvirtualenv.discovery\(dq] pyenv = \(dqvirtualenv_pyenv:PyEnvDiscovery\(dq [build\-system] requires = [\(dqsetuptools>=61\(dq] build\-backend = \(dqsetuptools.build_meta\(dq .EE .UNINDENT .UNINDENT .SS Implement the plugin .sp In \fBsrc/virtualenv_pyenv/__init__.py\fP, implement the discovery plugin by subclassing \fBDiscover\fP: .INDENT 0.0 .INDENT 3.5 .sp .EX from __future__ import annotations import subprocess from argparse import ArgumentParser from pathlib import Path from virtualenv.config.cli.parser import VirtualEnvOptions from virtualenv.discovery.discover import Discover from virtualenv.discovery.py_info import PythonInfo class PyEnvDiscovery(Discover): def __init__(self, options: VirtualEnvOptions) \-> None: super().__init__(options) self.python_spec = options.python if options.python else \(dqpython\(dq @classmethod def add_parser_arguments(cls, parser: ArgumentParser) \-> None: parser.add_argument( \(dq\-\-python\(dq, dest=\(dqpython\(dq, metavar=\(dqpy\(dq, type=str, default=None, help=\(dqpyenv Python version to use (e.g., 3.11.0)\(dq, ) def run(self) \-> PythonInfo | None: try: result = subprocess.run( [\(dqpyenv\(dq, \(dqwhich\(dq, \(dqpython\(dq], capture_output=True, text=True, check=True, ) python_path = Path(result.stdout.strip()) return PythonInfo.from_exe(str(python_path)) except (subprocess.CalledProcessError, FileNotFoundError) as e: raise RuntimeError(f\(dqFailed to locate pyenv Python: {e}\(dq) from e .EE .UNINDENT .UNINDENT .SS Install the plugin .sp Install your plugin in development mode alongside virtualenv: .INDENT 0.0 .INDENT 3.5 .sp .EX $ pip install \-e virtualenv\-pyenv/ .EE .UNINDENT .UNINDENT .SS Verify the plugin .sp Check that virtualenv recognizes your plugin by running: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-discovery help .EE .UNINDENT .UNINDENT .sp The output should list \fBpyenv\fP as an available discovery mechanism. You can now use it: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-discovery=pyenv myenv created virtual environment CPython3.11.0.final.0\-64 in 234ms creator CPython3Posix(dest=/path/to/myenv, clear=False, no_vcs_ignore=False, global=False) seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/path) added seed packages: pip==23.0, setuptools==65.5.0, wheel==0.38.4 activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator .EE .UNINDENT .UNINDENT .SS Plugin how\-to guides .sp This page provides task\-oriented guides for creating each type of virtualenv plugin. .SS Create a discovery plugin .sp Discovery plugins locate Python interpreters. Register your plugin under the \fBvirtualenv.discovery\fP entry point group. .sp Implement the \fBDiscover\fP interface: .INDENT 0.0 .INDENT 3.5 .sp .EX from __future__ import annotations from argparse import ArgumentParser from virtualenv.config.cli.parser import VirtualEnvOptions from virtualenv.discovery.discover import Discover from virtualenv.discovery.py_info import PythonInfo class CustomDiscovery(Discover): @classmethod def add_parser_arguments(cls, parser: ArgumentParser) \-> None: parser.add_argument(\(dq\-\-custom\-opt\(dq, help=\(dqcustom discovery option\(dq) def __init__(self, options: VirtualEnvOptions) \-> None: super().__init__(options) self.custom_opt = options.custom_opt def run(self) \-> PythonInfo | None: # Locate Python interpreter and return PythonInfo return PythonInfo.from_exe(str(self._find_python())) def _find_python(self) \-> str: # Implementation\-specific logic ... .EE .UNINDENT .UNINDENT .sp Register the entry point: .INDENT 0.0 .INDENT 3.5 .sp .EX [virtualenv.discovery] custom = your_package.discovery:CustomDiscovery .EE .UNINDENT .UNINDENT .SS Create a creator plugin .sp Creator plugins build the virtual environment structure. Register under \fBvirtualenv.create\fP\&. .sp Implement the \fBCreator\fP interface: .INDENT 0.0 .INDENT 3.5 .sp .EX from __future__ import annotations from argparse import ArgumentParser from virtualenv.app_data.base import AppData from virtualenv.config.cli.parser import VirtualEnvOptions from virtualenv.create.creator import Creator, CreatorMeta from virtualenv.discovery.py_info import PythonInfo class CustomCreator(Creator): @classmethod def add_parser_arguments( cls, parser: ArgumentParser, interpreter: PythonInfo, meta: CreatorMeta, app_data: AppData, ) \-> None: parser.add_argument(\(dq\-\-custom\-creator\-opt\(dq, help=\(dqcustom creator option\(dq) def __init__(self, options: VirtualEnvOptions, interpreter: PythonInfo) \-> None: super().__init__(options, interpreter) self.custom_opt = options.custom_creator_opt def create(self) \-> None: # Create directory structure self.bin_dir.mkdir(parents=True, exist_ok=True) # Copy or symlink Python executable self.install_python() # Set up site\-packages self.install_site_packages() # Write pyvenv.cfg self.set_pyenv_cfg() .EE .UNINDENT .UNINDENT .sp Register the entry point using a naming pattern that matches platform and Python version: .INDENT 0.0 .INDENT 3.5 .sp .EX [virtualenv.create] cpython3\-posix = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Posix cpython3\-win = virtualenv.create.via_global_ref.builtin.cpython.cpython3:CPython3Windows .EE .UNINDENT .UNINDENT .SS Create a seeder plugin .sp Seeder plugins install initial packages into the virtual environment. Register under \fBvirtualenv.seed\fP\&. .sp Override \fBcannot_seed\fP to reject target interpreters the seeder does not support. The base returns \fBNone\fP for every interpreter; return a message instead and selection rejects the seeder before creating the environment, surfacing your message to the user. A plugin can therefore serve Python versions the bundled seeders no longer ship wheels for, such as a version past its support window. .sp Implement the \fBSeeder\fP interface: .INDENT 0.0 .INDENT 3.5 .sp .EX from __future__ import annotations from argparse import ArgumentParser from virtualenv.app_data.base import AppData from virtualenv.config.cli.parser import VirtualEnvOptions from virtualenv.create.creator import Creator from virtualenv.discovery.py_info import PythonInfo from virtualenv.seed.seeder import Seeder class CustomSeeder(Seeder): @classmethod def add_parser_arguments( cls, parser: ArgumentParser, interpreter: PythonInfo, app_data: AppData ) \-> None: parser.add_argument(\(dq\-\-custom\-seed\-opt\(dq, help=\(dqcustom seeder option\(dq) @classmethod def cannot_seed(cls, interpreter: PythonInfo) \-> str | None: # ship wheels down to Python 3.6, for example if interpreter.version_info[:2] >= (3, 6): return None return \(dqcustom seeder ships wheels only for Python 3.6 and later\(dq def __init__(self, options: VirtualEnvOptions, enabled: bool) \-> None: super().__init__(options, enabled) self.custom_opt = options.custom_seed_opt def run(self, creator: Creator) \-> None: # Install packages into creator.bin_dir / creator.script(\(dqpip\(dq) self._install_packages(creator) def _install_packages(self, creator: Creator) \-> None: # Implementation\-specific logic ... .EE .UNINDENT .UNINDENT .sp Register the entry point: .INDENT 0.0 .INDENT 3.5 .sp .EX [virtualenv.seed] custom = your_package.seed:CustomSeeder .EE .UNINDENT .UNINDENT .SS Create an activator plugin .sp Activator plugins generate shell activation scripts. Register under \fBvirtualenv.activate\fP\&. .sp Implement the \fBActivator\fP interface: .INDENT 0.0 .INDENT 3.5 .sp .EX from __future__ import annotations from pathlib import Path from virtualenv.activation.activator import Activator from virtualenv.create.creator import Creator class CustomShellActivator(Activator): def generate(self, creator: Creator) \-> list[Path]: # Generate activation script content script_content = self._render_template(creator) # Write to activation directory dest = creator.bin_dir / self.script_name dest.write_text(script_content) return [dest] def _render_template(self, creator: Creator) \-> str: # Return activation script content return f\(dq\(dq\(dq # Custom shell activation script export VIRTUAL_ENV=\(dq{creator.dest}\(dq export PATH=\(dq{creator.bin_dir}:$PATH\(dq \(dq\(dq\(dq @property def script_name(self) \-> str: return \(dqactivate.custom\(dq .EE .UNINDENT .UNINDENT .sp Register the entry point: .INDENT 0.0 .INDENT 3.5 .sp .EX [virtualenv.activate] bash = virtualenv.activation.bash:BashActivator fish = virtualenv.activation.fish:FishActivator custom = your_package.activation:CustomShellActivator .EE .UNINDENT .UNINDENT .SS Package and distribute a plugin .sp Use \fBpyproject.toml\fP to declare entry points: .INDENT 0.0 .INDENT 3.5 .sp .EX [project] name = \(dqvirtualenv\-custom\-plugin\(dq version = \(dq1.0.0\(dq dependencies = [\(dqvirtualenv>=20.0.0\(dq] [project.entry\-points.\(dqvirtualenv.discovery\(dq] custom = \(dqvirtualenv_custom.discovery:CustomDiscovery\(dq [project.entry\-points.\(dqvirtualenv.create\(dq] custom\-posix = \(dqvirtualenv_custom.creator:CustomCreator\(dq [project.entry\-points.\(dqvirtualenv.seed\(dq] custom = \(dqvirtualenv_custom.seeder:CustomSeeder\(dq [project.entry\-points.\(dqvirtualenv.activate\(dq] custom = \(dqvirtualenv_custom.activator:CustomActivator\(dq [build\-system] requires = [\(dqsetuptools>=61\(dq] build\-backend = \(dqsetuptools.build_meta\(dq .EE .UNINDENT .UNINDENT .sp Install your plugin alongside virtualenv: .INDENT 0.0 .INDENT 3.5 .sp .EX $ pip install virtualenv\-custom\-plugin .EE .UNINDENT .UNINDENT .sp Or in development mode: .INDENT 0.0 .INDENT 3.5 .sp .EX $ pip install \-e /path/to/virtualenv\-custom\-plugin .EE .UNINDENT .UNINDENT .sp Test your plugin by creating a virtual environment: .INDENT 0.0 .INDENT 3.5 .sp .EX $ virtualenv \-\-discovery=custom \-\-creator=custom\-posix \-\-seeder=custom \-\-activators=custom test\-env .EE .UNINDENT .UNINDENT .SS Plugin API reference .sp This page documents the interfaces that plugins must implement. .SS Discovery .sp Discovery plugins locate Python interpreters for creating virtual environments. .INDENT 0.0 .TP .B class virtualenv.discovery.discover.Discover(options) .INDENT 7.0 .TP .B Parameters \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) .UNINDENT .INDENT 7.0 .TP .B classmethod add_parser_arguments(parser) .INDENT 7.0 .TP .B Parameters \fBparser\fP (\fBArgumentParser\fP \%) .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod run() .INDENT 7.0 .TP .B Return type \fBPythonInfo\fP | \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property interpreter: PythonInfo \%<#\:virtualenv\:.discovery\:.py_info\:.PythonInfo> | None \% .INDENT 7.0 .TP .B Returns the interpreter as returned by \fBrun()\fP, cached .UNINDENT .UNINDENT .UNINDENT .SS PythonInfo .sp Discovery plugins return a \fBPythonInfo\fP object describing the located interpreter. .INDENT 0.0 .TP .B class virtualenv.discovery.py_info.PythonInfo Contains information for a Python interpreter. .INDENT 7.0 .TP .B install_path(key) Return the relative installation path for a given installation scheme \fIkey\fP\&. .INDENT 7.0 .TP .B Parameters \fBkey\fP (\fBstr\fP \%) \-\- sysconfig installation scheme key (e.g. \fB\(dqscripts\(dq\fP, \fB\(dqpurelib\(dq\fP). .TP .B Return type \fBstr\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property version_str: str \% The full version as \fBmajor.minor.micro\fP string (e.g. \fB3.13.2\fP). .UNINDENT .INDENT 7.0 .TP .B property version_release_str: str \% The release version as \fBmajor.minor\fP string (e.g. \fB3.13\fP). .UNINDENT .INDENT 7.0 .TP .B property python_name: str \% The python executable name as \fBpythonX.Y\fP (e.g. \fBpython3.13\fP). .UNINDENT .INDENT 7.0 .TP .B property is_old_virtualenv: bool \% \fBTrue\fP if this interpreter runs inside an old\-style virtualenv (has \fBreal_prefix\fP). .UNINDENT .INDENT 7.0 .TP .B property is_venv: bool \% \fBTrue\fP if this interpreter runs inside a PEP 405 venv (has \fBbase_prefix\fP). .UNINDENT .INDENT 7.0 .TP .B sysconfig_path(key, config_var=None, sep=\(aq/\(aq) Return the sysconfig install path for a scheme \fIkey\fP, optionally substituting config variables. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBkey\fP (\fBstr\fP \%) \-\- sysconfig path key (e.g. \fB\(dqpurelib\(dq\fP, \fB\(dqinclude\(dq\fP). .IP \(bu 2 \fBconfig_var\fP (\fBdict\fP \%[\fBstr\fP \%, \fBstr\fP \%] | \fBNone\fP \%) \-\- replacement mapping for sysconfig variables; when \fBNone\fP uses the interpreter\(aqs own values. .IP \(bu 2 \fBsep\fP (\fBstr\fP \%) \-\- path separator to use in the result. .UNINDENT .TP .B Return type \fBstr\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property system_include: str \% The path to the system include directory for C headers. .UNINDENT .INDENT 7.0 .TP .B property system_prefix: str \% The prefix of the system Python this interpreter is based on. .UNINDENT .INDENT 7.0 .TP .B property system_exec_prefix: str \% The exec prefix of the system Python this interpreter is based on. .UNINDENT .INDENT 7.0 .TP .B property machine: str \% Return the instruction set architecture (ISA) derived from \fBsysconfig.get_platform()\fP \%\&. .UNINDENT .INDENT 7.0 .TP .B property spec: str \% A specification string identifying this interpreter (e.g. \fBCPython3.13.2\-64\-arm64\fP). .UNINDENT .INDENT 7.0 .TP .B classmethod clear_cache(cache) Clear all cached interpreter information from \fIcache\fP\&. .INDENT 7.0 .TP .B Parameters \fBcache\fP (PyInfoCache) \-\- the cache store to clear. .TP .B Return type None .UNINDENT .UNINDENT .INDENT 7.0 .TP .B satisfies(spec, *, impl_must_match) Check if a given specification can be satisfied by this python interpreter instance. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBspec\fP (PythonSpec) \-\- the specification to check against. .IP \(bu 2 \fBimpl_must_match\fP (bool) \-\- when \fBTrue\fP, the implementation name must match exactly. .UNINDENT .TP .B Return type bool .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod current(cache=None) Locate the current host interpreter information. .INDENT 7.0 .TP .B Parameters \fBcache\fP (PyInfoCache | None) \-\- interpreter metadata cache; when \fBNone\fP results are not cached. .TP .B Return type PythonInfo .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod current_system(cache=None) Locate the current system interpreter information, resolving through any virtualenv layers. .INDENT 7.0 .TP .B Parameters \fBcache\fP (PyInfoCache | None) \-\- interpreter metadata cache; when \fBNone\fP results are not cached. .TP .B Return type PythonInfo .UNINDENT .UNINDENT .INDENT 7.0 .TP .B to_json() Serialize this interpreter information to a JSON string. .INDENT 7.0 .TP .B Return type \fBstr\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B to_dict() Convert this interpreter information to a plain dictionary. .INDENT 7.0 .TP .B Return type \fBdict\fP \%[\fBstr\fP \%, \fBobject\fP \%] .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod from_exe(exe, cache=None, *, raise_on_error=True, ignore_cache=False, resolve_to_host=True, env=None) Get the python information for a given executable path. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBexe\fP (str) \-\- path to the Python executable. .IP \(bu 2 \fBcache\fP (PyInfoCache | None) \-\- interpreter metadata cache; when \fBNone\fP results are not cached. .IP \(bu 2 \fBraise_on_error\fP (bool) \-\- raise on failure instead of returning \fBNone\fP\&. .IP \(bu 2 \fBignore_cache\fP (bool) \-\- bypass the cache and re\-query the interpreter. .IP \(bu 2 \fBresolve_to_host\fP (bool) \-\- resolve through virtualenv layers to the system interpreter. .IP \(bu 2 \fBenv\fP (Mapping[str, str] | None) \-\- environment mapping; defaults to \fBos.environ\fP \%\&. .UNINDENT .TP .B Return type PythonInfo | None .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod from_json(payload) Deserialize interpreter information from a JSON string. .INDENT 7.0 .TP .B Parameters \fBpayload\fP (\fBstr\fP \%) \-\- JSON produced by \fBto_json()\fP\&. .TP .B Return type \fBPythonInfo\fP .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod from_dict(data) Reconstruct a \fBPythonInfo\fP from a plain dictionary. .INDENT 7.0 .TP .B Parameters \fBdata\fP (\fBdict\fP \%[\fBstr\fP \%, \fBobject\fP \%]) \-\- dictionary produced by \fBto_dict()\fP\&. .TP .B Return type \fBPythonInfo\fP .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod resolve_to_system(cache, target) Walk virtualenv/venv prefix chains to find the underlying system interpreter. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcache\fP (PyInfoCache | None) \-\- interpreter metadata cache; when \fBNone\fP results are not cached. .IP \(bu 2 \fBtarget\fP (PythonInfo) \-\- the interpreter to resolve. .UNINDENT .TP .B Return type PythonInfo .UNINDENT .UNINDENT .INDENT 7.0 .TP .B discover_exe(cache, prefix, *, exact=True, env=None) Discover a matching Python executable under a given \fIprefix\fP directory. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBcache\fP (PyInfoCache) \-\- interpreter metadata cache. .IP \(bu 2 \fBprefix\fP (str) \-\- directory prefix to search under. .IP \(bu 2 \fBexact\fP (bool) \-\- when \fBTrue\fP, require an exact version match. .IP \(bu 2 \fBenv\fP (Mapping[str, str] | None) \-\- environment mapping; defaults to \fBos.environ\fP \%\&. .UNINDENT .TP .B Return type PythonInfo .UNINDENT .UNINDENT .UNINDENT .SS App data .sp The application data interface used by plugins for caching. .INDENT 0.0 .TP .B class virtualenv.app_data.base.AppData Abstract storage interface for the virtualenv application. .INDENT 7.0 .TP .B abstractmethod close() Called before virtualenv exits. .INDENT 7.0 .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod reset() Called when the user passes in the reset app data. .INDENT 7.0 .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod py_info(path) Return a content store for cached interpreter information at the given path. .INDENT 7.0 .TP .B Parameters \fBpath\fP (\fBPath\fP \%) \-\- the interpreter executable path .TP .B Return type \fBContentStore\fP .TP .B Returns a content store for the cached data .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod py_info_clear() Clear all cached interpreter information. .INDENT 7.0 .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property can_update: bool \% \fBTrue\fP if this app data store supports updating cached content. .UNINDENT .INDENT 7.0 .TP .B abstractmethod embed_update_log(distribution, for_py_version) Return a content store for the embed update log of a distribution. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBdistribution\fP (\fBstr\fP \%) \-\- the package name (e.g. \fBpip\fP) .IP \(bu 2 \fBfor_py_version\fP (\fBstr\fP \%) \-\- the target Python version string .UNINDENT .TP .B Return type \fBContentStore\fP .TP .B Returns a content store for the update log .UNINDENT .UNINDENT .INDENT 7.0 .TP .B property house: Path \% The root directory of the application data store. .UNINDENT .INDENT 7.0 .TP .B property transient: bool \% \fBTrue\fP if this app data store is transient and does not persist across runs. .UNINDENT .INDENT 7.0 .TP .B abstractmethod wheel_image(for_py_version, name) Return the path to a cached wheel image. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBfor_py_version\fP (\fBstr\fP \%) \-\- the target Python version string .IP \(bu 2 \fBname\fP (\fBstr\fP \%) \-\- the package name .UNINDENT .TP .B Return type \fBPath\fP \% .TP .B Returns the path to the cached wheel .UNINDENT .UNINDENT .INDENT 7.0 .TP .B ensure_extracted(path, to_folder=None) Ensure a path is available on disk, extracting from zipapp if needed. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpath\fP (\fBPath\fP \%) \-\- the path to ensure is available .IP \(bu 2 \fBto_folder\fP (\fBPath\fP \% | \fBNone\fP \%) \-\- optional target directory for extraction .UNINDENT .TP .B Return type \fBGenerator\fP \%[\fBPath\fP \%] .TP .B Returns yields the usable path on disk .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod extract(path, to_folder) Extract a path from the zipapp to a location on disk. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBpath\fP (\fBPath\fP \%) \-\- the path to extract .IP \(bu 2 \fBto_folder\fP (\fBPath\fP \% | \fBNone\fP \%) \-\- optional target directory .UNINDENT .TP .B Return type \fBGenerator\fP \%[\fBPath\fP \%] .TP .B Returns yields the extracted path .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod locked(path) Acquire an exclusive lock on the given path. .INDENT 7.0 .TP .B Parameters \fBpath\fP (\fBPath\fP \%) \-\- the path to lock .TP .B Return type \fBGenerator\fP \%[\fBNone\fP \%] .UNINDENT .UNINDENT .UNINDENT .SS Creators .sp Creator plugins build the virtual environment directory structure and install the Python interpreter. .INDENT 0.0 .TP .B class virtualenv.create.creator.CreatorMeta .UNINDENT .INDENT 0.0 .TP .B class virtualenv.create.creator.Creator(options, interpreter) A class that given a python Interpreter creates a virtual environment. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) .IP \(bu 2 \fBinterpreter\fP (\fBPythonInfo\fP) .UNINDENT .UNINDENT .sp Construct a new virtual environment creator. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) \-\- the CLI option as parsed from \fBadd_parser_arguments()\fP .IP \(bu 2 \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter to create virtual environment from .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod can_create(interpreter) Determine if we can create a virtual environment. .INDENT 7.0 .TP .B Parameters \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter in question .TP .B Return type \fBCreatorMeta\fP | \fBbool\fP \% | \fBNone\fP \% .TP .B Returns \fBNone\fP if we can\(aqt create, any other object otherwise that will be forwarded to \fBadd_parser_arguments()\fP .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod add_parser_arguments(parser, interpreter, meta, app_data) Add CLI arguments for the creator. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBparser\fP (\fBArgumentParser\fP \%) \-\- the CLI parser .IP \(bu 2 \fBapp_data\fP (\fBAppData\fP) \-\- the application data folder .IP \(bu 2 \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter we\(aqre asked to create virtual environment for .IP \(bu 2 \fBmeta\fP (\fBCreatorMeta\fP) \-\- value as returned by \fBcan_create()\fP .UNINDENT .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod create() Perform the virtual environment creation. .INDENT 7.0 .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B add_cachedir_tag() Generate a file indicating that this is not meant to be backed up. .INDENT 7.0 .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B setup_ignore_vcs() Generate ignore instructions for version control systems. .INDENT 7.0 .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .UNINDENT .SS Seeders .sp Seeder plugins install initial packages (like pip, setuptools, wheel) into the virtual environment. .INDENT 0.0 .TP .B class virtualenv.seed.seeder.Seeder(options, enabled) A seeder will install some seed packages into a virtual environment. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) .IP \(bu 2 \fBenabled\fP (\fBbool\fP \%) .UNINDENT .UNINDENT .sp Create. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) \-\- the parsed options as defined within \fBadd_parser_arguments()\fP .IP \(bu 2 \fBenabled\fP (\fBbool\fP \%) \-\- a flag weather the seeder is enabled or not .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod cannot_seed(interpreter) Explain why this seeder cannot install seed packages for the given interpreter. .INDENT 7.0 .TP .B Parameters \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter the environment is based on .TP .B Return type \fBstr\fP \% | \fBNone\fP \% .TP .B Returns \fBNone\fP when the seeder supports the interpreter, otherwise a message describing why it cannot; selection rejects a seeder that returns a message and surfaces it to the user .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod add_parser_arguments(parser, interpreter, app_data) Add CLI arguments for this seed mechanisms. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBparser\fP (\fBArgumentParser\fP \%) \-\- the CLI parser .IP \(bu 2 \fBapp_data\fP (\fBAppData\fP) \-\- the CLI parser .IP \(bu 2 \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter this virtual environment is based of .UNINDENT .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod run(creator) Perform the seed operation. .INDENT 7.0 .TP .B Parameters \fBcreator\fP (\fBCreator\fP) \-\- the creator (based of \fBvirtualenv.create.creator.Creator\fP) we used to create this virtual environment .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .UNINDENT .SS Activators .sp Activator plugins generate shell\-specific activation scripts. .INDENT 0.0 .TP .B class virtualenv.activation.activator.Activator(options) Generates activate script for the virtual environment. .INDENT 7.0 .TP .B Parameters \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) .UNINDENT .sp Create a new activator generator. .INDENT 7.0 .TP .B Parameters \fBoptions\fP (\fBVirtualEnvOptions\fP \%<#\:virtualenv\:.config\:.cli\:.parser\:.VirtualEnvOptions>) \-\- the parsed options as defined within \fBadd_parser_arguments()\fP .UNINDENT .INDENT 7.0 .TP .B classmethod supports(interpreter) Check if the activation script is supported in the given interpreter. .INDENT 7.0 .TP .B Parameters \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter we need to support .TP .B Return type \fBbool\fP \% .TP .B Returns \fBTrue\fP if supported, \fBFalse\fP otherwise .UNINDENT .UNINDENT .INDENT 7.0 .TP .B classmethod add_parser_arguments(parser, interpreter) Add CLI arguments for this activation script. .INDENT 7.0 .TP .B Parameters .INDENT 7.0 .IP \(bu 2 \fBparser\fP (\fBArgumentParser\fP \%) \-\- the CLI parser .IP \(bu 2 \fBinterpreter\fP (\fBPythonInfo\fP) \-\- the interpreter this virtual environment is based of .UNINDENT .TP .B Return type \fBNone\fP \% .UNINDENT .UNINDENT .INDENT 7.0 .TP .B abstractmethod generate(creator) Generate activate script for the given creator. .INDENT 7.0 .TP .B Parameters \fBcreator\fP (\fBCreator\fP) \-\- the creator (based of \fBvirtualenv.create.creator.Creator\fP) we used to create this virtual environment .TP .B Return type \fBlist\fP \%[\fBPath\fP \%] .UNINDENT .UNINDENT .UNINDENT .SS Plugin architecture .sp This page explains how virtualenv\(aqs plugin system works internally. .SS Entry points .sp virtualenv uses Python entry points (\fBsetuptools\fP / \fBimportlib.metadata\fP) to discover plugins. Each plugin registers under one of four entry point groups: .INDENT 0.0 .IP \(bu 2 \fBvirtualenv.discovery\fP .IP \(bu 2 \fBvirtualenv.create\fP .IP \(bu 2 \fBvirtualenv.seed\fP .IP \(bu 2 \fBvirtualenv.activate\fP .UNINDENT .sp At startup, virtualenv loads all registered entry points from these groups and makes them available as CLI options. Built\-in implementations are registered in virtualenv\(aqs own \fBpyproject.toml\fP, while third\-party plugins register their entry points in their own package metadata. .sp When a package with virtualenv plugins is installed in the same environment as virtualenv, the plugins become immediately available without additional configuration. .SS Plugin lifecycle .sp The following diagram shows how plugins are discovered and executed: [graph] .sp The lifecycle follows these stages: .INDENT 0.0 .IP 1. 3 virtualenv starts and discovers all entry points from the four plugin groups .IP 2. 3 The CLI parser is built dynamically, incorporating options from all discovered plugins .IP 3. 3 User arguments are parsed to select which discovery, creator, seeder, and activator plugins to use .IP 4. 3 Selected plugins execute in sequence: discover → create → seed → activate .IP 5. 3 Each stage passes its output to the next stage .UNINDENT .SS Extension point design .sp Each extension point follows a consistent pattern: .INDENT 0.0 .TP .B Base abstract class Each extension point defines a base abstract class (\fBDiscover\fP, \fBCreator\fP, \fBSeeder\fP, \fBActivator\fP) that specifies the interface plugins must implement. .TP .B Built\-in implementations virtualenv includes built\-in implementations registered as entry points in its own \fBpyproject.toml\fP\&. For example, the built\-in CPython creator is registered as \fBcpython3\-posix\fP\&. .TP .B Third\-party plugins External packages implement the base interface and register their own entry points under the same group. When installed, they appear alongside built\-in options. .TP .B CLI selection Command\-line flags (\fB\-\-discovery\fP, \fB\-\-creator\fP, \fB\-\-seeder\fP, \fB\-\-activators\fP) allow users to select which implementation to use. Multiple activators can be selected simultaneously. .TP .B Parser integration Each plugin can contribute CLI arguments through the \fBadd_parser_arguments\fP classmethod. These arguments appear in \fBvirtualenv \-\-help\fP and are available when the plugin is selected. .UNINDENT .SS How plugins interact .sp Plugins execute in a pipeline where each stage depends on the previous one: .INDENT 0.0 .TP .B Discovery → Creator The discovery plugin produces a \fBPythonInfo\fP object describing the source Python interpreter. This object contains metadata about the Python version, platform, paths, and capabilities. The creator plugin receives this \fBPythonInfo\fP and uses it to determine how to build the virtual environment structure. .TP .B Creator → Seeder The creator plugin produces a \fBCreator\fP object representing the newly created virtual environment. This includes paths to the environment\(aqs \fBbin\fP directory, site\-packages, and Python executable. The seeder plugin uses these paths to install packages. .TP .B Seeder → Activator After seeding completes, activator plugins use the \fBCreator\fP object to generate shell activation scripts. These scripts reference the environment\(aqs bin directory and other paths to configure the shell environment. .UNINDENT .sp This pipeline ensures that each plugin has the information it needs from previous stages. The \fBPythonInfo\fP flows from discovery to creator, and the \fBCreator\fP object flows from creator to both seeder and activators. .SS Plugin isolation .sp Plugins within the same extension point do not interact with each other. Only one discovery and one creator plugin can run per invocation, though multiple activators can run simultaneously. This isolation keeps plugins simple and focused on their specific task. .SS Development .SS Getting started .sp \fBvirtualenv\fP is a volunteer maintained open source project and we welcome contributions of all forms. The sections below will help you get started with development, testing, and documentation. We’re pleased that you are interested in working on virtualenv. This document is meant to get you setup to work on virtualenv and to act as a guide and reference to the development setup. If you face any issues during this process, please open an issue \% about it on the issue tracker. .SS Setup .sp virtualenv is a command line application written in Python. To work on it, you\(aqll need: .INDENT 0.0 .IP \(bu 2 .INDENT 2.0 .TP \fBSource code\fP: available on GitHub \%\&. You can use \fBgit\fP to clone the repository: .UNINDENT .INDENT 2.0 .INDENT 3.5 .sp .EX git clone https://github.com/pypa/virtualenv cd virtualenv .EE .UNINDENT .UNINDENT .IP \(bu 2 \fBPython interpreter\fP: We recommend using \fBCPython\fP\&. You can use this guide \% to set it up. .IP \(bu 2 tox \%: to automatically get the projects development dependencies and run the test suite. We recommend installing it using pipx \%\&. .UNINDENT .SS Running from source tree .sp The easiest way to do this is to generate the development tox environment, and then invoke virtualenv from under the \fB\&.tox/dev\fP folder .INDENT 0.0 .INDENT 3.5 .sp .EX tox \-e dev \&.tox/dev/bin/virtualenv # on Linux \&.tox/dev/Scripts/virtualenv # on Windows .EE .UNINDENT .UNINDENT .SS Running tests .sp virtualenv\(aqs tests are written using the pytest \% test framework. tox \% is used to automate the setup and execution of virtualenv\(aqs tests. .sp To run tests locally execute: .INDENT 0.0 .INDENT 3.5 .sp .EX tox \-e py .EE .UNINDENT .UNINDENT .sp This will run the test suite for the same Python version as under which \fBtox\fP is installed. Alternatively you can specify a specific version of python by using the \fBpyNN\fP format, such as: \fBpy314\fP, \fBpypy3\fP, etc. .sp \fBtox\fP has been configured to forward any additional arguments it is given to \fBpytest\fP\&. This enables the use of pytest\(aqs rich CLI \%\&. As an example, you can select tests using the various ways that pytest provides: .INDENT 0.0 .INDENT 3.5 .sp .EX # Using markers tox \-e py \-\- \-m \(dqnot slow\(dq # Using keywords tox \-e py \-\- \-k \(dqtest_extra\(dq .EE .UNINDENT .UNINDENT .sp Some tests require additional dependencies to be run, such is the various shell activators (\fBbash\fP, \fBfish\fP, \fBpowershell\fP, etc). These tests will automatically be skipped if these are not present, note however that in CI all tests are run; so even if all tests succeed locally for you, they may still fail in the CI. .SS Running linters .sp virtualenv uses pre\-commit \% for managing linting of the codebase. \fBpre\-commit\fP performs various checks on all files in virtualenv and uses tools that help follow a consistent code style within the codebase. To use linters locally, run: .INDENT 0.0 .INDENT 3.5 .sp .EX tox \-e fix .EE .UNINDENT .UNINDENT .sp \fBNote:\fP .INDENT 0.0 .INDENT 3.5 Avoid using \fB# noqa\fP comments to suppress linter warnings \- wherever possible, warnings should be fixed instead. \fB# noqa\fP comments are reserved for rare cases where the recommended style causes severe readability problems. .UNINDENT .UNINDENT .SS Type checking .sp virtualenv ships a \fBPEP 561\fP \% \fBpy.typed\fP marker and has comprehensive type annotations across the entire codebase. This means downstream consumers and type checkers automatically recognize virtualenv as an inline\-typed package. .sp All new code \fBmust\fP include complete type annotations for function parameters and return types. To verify annotations locally, run: .INDENT 0.0 .INDENT 3.5 .sp .EX tox \-e type .EE .UNINDENT .UNINDENT .sp This uses ty \% (Astral\(aqs Rust\-based type checker) to validate annotations against Python 3.14. A second environment checks compatibility with the minimum supported version: .INDENT 0.0 .INDENT 3.5 .sp .EX tox \-e type\-3.9 .EE .UNINDENT .UNINDENT .sp Both environments validate that annotations are consistent and correct. .SS Annotation guidelines .INDENT 0.0 .IP \(bu 2 Use \fBfrom __future__ import annotations\fP at the top of every module (enforced by ruff\(aqs \fBrequired\-imports\fP setting). .IP \(bu 2 Place imports that are only needed for type checking inside an \fBif TYPE_CHECKING:\fP block to avoid runtime overhead. .IP \(bu 2 Ruff\(aqs \fBANN\fP rules are enabled. \fBANN401\fP (\fBtyping.Any\fP) is suppressed on a case\-by\-case basis with inline \fB# noqa: ANN401\fP comments where \fBAny\fP is genuinely required (e.g. serialization, dynamic dispatch). .IP \(bu 2 Prefer concrete types over \fBAny\fP\&. Use \fBUnion\fP / \fB|\fP for nullable or multi\-type parameters. .IP \(bu 2 When a type error is genuinely unfixable (e.g. third\-party library limitations), suppress it with an inline \fB# ty: ignore[rule\-name]\fP comment and a brief justification. .UNINDENT .SS Building documentation .sp virtualenv\(aqs documentation is built using Sphinx \%\&. The documentation is written in reStructuredText. To build it locally, run: .INDENT 0.0 .INDENT 3.5 .sp .EX tox \-e docs .EE .UNINDENT .UNINDENT .sp The built documentation can be found in the \fB\&.tox/docs_out\fP folder and may be viewed by opening \fBindex.html\fP within that folder. .SS Release .sp virtualenv\(aqs release schedule is tied to \fBpip\fP and \fBsetuptools\fP\&. We bundle the latest version of these libraries so each time there\(aqs a new version of any of these, there will be a new virtualenv release shortly afterwards (we usually wait just a few days to avoid pulling in any broken releases). .SS Performing a release .sp A full release publishes to PyPI \%, creates a GitHub Release \% with the zipapp attached, and updates get\-virtualenv \% so that \fBhttps://bootstrap.pypa.io/virtualenv.pyz\fP serves the new version. .SS Version bumping .sp The \fB\-\-version\fP argument to \fBtox r \-e release\fP controls the version. It defaults to \fBauto\fP, which inspects the \fBdocs/changelog\fP directory: if any \fB*.feature.rst\fP or \fB*.removal.rst\fP fragments exist, the minor version is bumped, otherwise the patch version is bumped. You can also pass \fBmajor\fP, \fBminor\fP, or \fBpatch\fP explicitly. .sp Both methods produce identical results: a release commit and tag on \fBmain\fP\&. Pushing the tag triggers the Release workflow \% which builds the sdist, wheel, and zipapp, publishes to PyPI via trusted publisher, creates a GitHub Release \% with the zipapp attached, and updates get\-virtualenv \%\&. If publish fails, a rollback job automatically reverts everything. .sp \fBVia GitHub Actions (recommended)\fP .INDENT 0.0 .IP 1. 3 Go to the Pre\-release workflow \% on GitHub. .IP 2. 3 Click \fBRun workflow\fP and select the bump type (\fBauto\fP, \fBmajor\fP, \fBminor\fP, or \fBpatch\fP). .UNINDENT .sp \fBLocally\fP .INDENT 0.0 .INDENT 3.5 .sp .EX tox r \-e release .EE .UNINDENT .UNINDENT .sp Pass \fB\-\-version \fP to override the default \fBauto\fP behavior (e.g. \fB\-\-version minor\fP). .SS Contributing .SS Submitting pull requests .sp Submit pull requests against the \fBmain\fP branch, providing a good description of what you\(aqre doing and why. You must have legal permission to distribute any code you contribute to virtualenv and it must be available under the MIT License. Provide tests that cover your changes and run the tests locally first. virtualenv supports \%<#\:compatibility-requirements> multiple Python versions and operating systems. Any pull request must consider and work on all these platforms. .sp Pull Requests should be small to facilitate review. Keep them self\-contained, and limited in scope. Studies have shown \% that review quality falls off as patch size grows. Sometimes this will result in many small PRs to land a single large feature. In particular, pull requests must not be treated as \(dqfeature branches\(dq, with ongoing development work happening within the PR. Instead, the feature should be broken up into smaller, independent parts which can be reviewed and merged individually. .sp Additionally, avoid including \(dqcosmetic\(dq changes to code that is unrelated to your change, as these make reviewing the PR more difficult. Examples include re\-flowing text in comments or documentation, or addition or removal of blank lines or whitespace within lines. Such changes can be made separately, as a \(dqformatting cleanup\(dq PR, if needed. .SS Automated testing .sp All pull requests and merges to \(aqmain\(aq branch are tested using GitHub Actions \% (configured by \fB\&.github/workflows/check.yaml\fP file at the root of the repository). You can find the status and results to the CI runs for your PR on GitHub\(aqs Web UI for the pull request. You can also find links to the CI services\(aq pages for the specific builds in the form of \(dqDetails\(dq links, in case the CI run fails and you wish to view the output. .sp To trigger CI to run again for a pull request, you can close and open the pull request or submit another change to the pull request. If needed, project maintainers can manually trigger a restart of a job/build. .SS NEWS entries .sp The \fBchangelog.rst\fP file is managed using towncrier \% and all non trivial changes must be accompanied by a news entry. To add an entry to the news file, first you need to have created an issue describing the change you want to make. A Pull Request itself \fImay\fP function as such, but it is preferred to have a dedicated issue (for example, in case the PR ends up rejected due to code quality reasons). .sp Once you have an issue or pull request, you take the number and you create a file inside of the \fBdocs/changelog\fP directory named after that issue number with an extension of: .INDENT 0.0 .IP \(bu 2 \fBfeature.rst\fP, .IP \(bu 2 \fBbugfix.rst\fP, .IP \(bu 2 \fBdoc.rst\fP, .IP \(bu 2 \fBremoval.rst\fP, .IP \(bu 2 \fBmisc.rst\fP\&. .UNINDENT .sp Thus if your issue or PR number is \fB1234\fP and this change is fixing a bug, then you would create a file \fBdocs/changelog/1234.bugfix.rst\fP\&. PRs can span multiple categories by creating multiple files (for instance, if you added a feature and deprecated/removed the old feature at the same time, you would create \fBdocs/changelog/1234.bugfix.rst\fP and \fBdocs/changelog/1234.remove.rst\fP). Likewise if a PR touches multiple issues/PRs you may create a file for each of them with the same contents and towncrier \% will deduplicate them. .SS Contents of a NEWS entry .sp The contents of this file are reStructuredText formatted text that will be used as the content of the news file entry. You do not need to reference the issue or PR numbers here as towncrier will automatically add a reference to all of the affected issues when rendering the news file. .sp In order to maintain a consistent style in the \fBchangelog.rst\fP file, it is preferred to keep the news entry to the point, in sentence case, shorter than 120 characters and in an imperative tone \-\- an entry should complete the sentence \fBThis change will …\fP\&. In rare cases, where one line is not enough, use a summary line in an imperative tone followed by a blank line separating it from a description of the feature/change in one or more paragraphs, each wrapped at 120 characters. Remember that a news entry is meant for end users and should only contain details relevant to an end user. .SS Choosing the type of NEWS entry .sp A trivial change is anything that does not warrant an entry in the news file. Some examples are: code refactors that don\(aqt change anything as far as the public is concerned, typo fixes, white space modification, etc. To mark a PR as trivial a contributor simply needs to add a randomly named, empty file to the \fBnews/\fP directory with the extension of \fB\&.trivial\fP\&. .SS Becoming a maintainer .sp If you want to become an official maintainer, start by helping out. As a first step, we welcome you to triage issues on virtualenv\(aqs issue tracker. virtualenv maintainers provide triage abilities to contributors once they have been around for some time and contributed positively to the project. This is optional and highly recommended for becoming a virtualenv maintainer. Later, when you think you\(aqre ready, get in touch with one of the maintainers and they will initiate a vote among the existing maintainers. .sp \fBNote:\fP .INDENT 0.0 .INDENT 3.5 Upon becoming a maintainer, a person should be given access to various virtualenv\-related tooling across multiple platforms. These are noted here for future reference by the maintainers: .INDENT 0.0 .IP \(bu 2 GitHub Push Access .IP \(bu 2 PyPI Publishing Access .IP \(bu 2 CI Administration capabilities .IP \(bu 2 ReadTheDocs Administration capabilities .UNINDENT .UNINDENT .UNINDENT .SS Current maintainers .INDENT 0.0 .IP \(bu 2 Bernát Gábor \% .IP \(bu 2 Rahul Devikar \% .UNINDENT .SS Previous maintainers .INDENT 0.0 .IP \(bu 2 Paul Moore \% .IP \(bu 2 Ian Bicking \% .IP \(bu 2 Donald Stufft \% .UNINDENT .SS Release History .SS v[UNRELEASED DRAFT] (2026\-06\-19) .sp No significant changes. .SS v21.5.1 (2026\-06\-16) .SS Bugfixes \- 21.5.1 .INDENT 0.0 .IP \(bu 2 Refuse to create environments whose Python the bundled wheels no longer cover (currently below 3.9). virtualenv used to substitute the newest bundled \fBpip\fP, which cannot run on such a target, leaving a broken environment; seeder selection now rejects it up front with a clear error. \fB\-\-no\-seed\fP and third\-party seeders that ship compatible wheels still work \- by @gaborbernat \%\&. (#3171 \%) .UNINDENT .SS v21.5.0 (2026\-06\-13) .SS Features \- 21.5.0 .INDENT 0.0 .IP \(bu 2 Drop support for Python 3.8; virtualenv now requires Python 3.9 or later to run and to create environments. Remove the embedded \fBwheel\fP seed package, which virtualenv bundled only for Python 3.8. The \fB\-\-wheel\fP and \fB\-\-no\-wheel\fP options stay as no\-ops, but now warn that virtualenv will remove them in a release after 2026\-12 \- by @gaborbernat \%\&. (#3170 \%) .UNINDENT .SS Bugfixes \- 21.5.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .sp Removed wheel of \fB0.47.0\fP (#u \%) .UNINDENT .SS v21.4.3 (2026\-06\-11) .SS Bugfixes \- 21.4.3 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB26.1.2\fP from \fB26.1.1\fP (#u \%) .UNINDENT .IP \(bu 2 Resolve executable\-only symlinks when recording \fBhome\fP and \fBbase\-executable\fP in \fBpyvenv.cfg\fP, mirroring CPython\(aqs \fBgetpath.realpath\fP (python/cpython#115237), so environments created from a symlink to the interpreter binary locate the base stdlib (for example python\-build\-standalone); a fully symlinked interpreter tree is kept as\-is \- by @gaborbernat \%\&. (#3157 \%) .IP \(bu 2 Stop exporting \fBPS1\fP from the bash activator so child processes do not inherit shell prompt state. (#3158 \%) .IP \(bu 2 Handle CYGWIN/MSYS/MINGW path conversions in fish activation script \- by user::\fILuNoX\fP\&. (#3160 \%) .UNINDENT .SS v21.4.2 (2026\-05\-31) .SS Bugfixes \- 21.4.2 .INDENT 0.0 .IP \(bu 2 Stop \fBdeactivate\fP in the bash/zsh activation script from aborting under \fBset \-e\fP when \fBhash \-r\fP fails (for example with shell hashing disabled) by appending \fB|| true\fP, matching CPython \fBvenv\fP (gh\-149701) and the existing non\-deactivate call \- by @gaborbernat \%\&. (#3152 \%) .UNINDENT .SS v21.4.1 (2026\-05\-28) .SS Bugfixes \- 21.4.1 .INDENT 0.0 .IP \(bu 2 Fix Windows debug build \fBvenvlauncher_d.exe\fP substitution never triggering because \fBexecutables()\fP compared the source executable name instead of the target name, and fix \fBAttributeError\fP on \fBdebug_build\fP attribute for interpreter info objects missing the field \- by @gaborbernat \%\&. (#3151 \%) .UNINDENT .SS v21.4.0 (2026\-05\-28) .SS Features \- 21.4.0 .INDENT 0.0 .IP \(bu 2 Remove dead code targeting Python versions below the supported target range (PyPy 3.6, deprecated importlib APIs) and simplify the runtime import hook in \fB_virtualenv.py\fP \- by @gaborbernat \%\&. (#3149 \%) .IP \(bu 2 Support Windows debug builds (\fBpython_d.exe\fP, \fBvenvlauncher_d.exe\fP) matching CPython venv behavior, remove dead \fB__SCRIPT_DIR__\fP replacement and \fBhas_shim\fP version guard, drop unreachable Python 3.7 branch from \fBpyvenv_launch_patch_active\fP, and fix wheel deprecation message to say \fB>= 3.9\fP \- by @gaborbernat \%\&. (#3150 \%) .UNINDENT .SS v21.3.3 (2026\-05\-13) .SS Bugfixes \- 21.3.3 .INDENT 0.0 .IP \(bu 2 recognize GraalPy interpreters using the normalized \fBGraalPy\fP name \- by @timfel \%\&. (#3144 \%) .UNINDENT .SS v21.3.2 (2026\-05\-12) .sp No significant changes. .SS v21.3.1 (2026\-05\-05) .SS Bugfixes \- 21.3.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB26.1.1\fP from \fB26.1\fP (#3138 \%) .UNINDENT .UNINDENT .SS v21.3.0 (2026\-04\-27) .SS Features \- 21.3.0 .INDENT 0.0 .IP \(bu 2 Re\-introduce \fBxonsh\fP shell activator (\fBactivate.xsh\fP) previously removed in 20.7.0, and make the plugin loader prefer virtualenv\(aqs built\-in entry points so a third\-party package cannot override them by registering a duplicate name. (#3003 \%) .UNINDENT .SS Bugfixes \- 21.3.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB26.1\fP (#3132 \%) .UNINDENT .UNINDENT .SS v21.2.4 (2026\-04\-14) .SS Bugfixes \- 21.2.4 .INDENT 0.0 .IP \(bu 2 Security hardening: validate each entry of a seed wheel archive before extracting it so a tampered wheel cannot escape the app\-data image directory via an absolute path or \fB\&..\fP traversal. (#3118 \%) .IP \(bu 2 Security hardening: verify the SHA\-256 of every bundled seed wheel when it is loaded so a corrupted or tampered file on disk fails loud instead of being handed to pip. The hash table is generated alongside \fBBUNDLE_SUPPORT\fP by \fBtasks/upgrade_wheels.py\fP\&. (#3119 \%) .IP \(bu 2 Security hardening: validate the distribution name and version specifier passed to \fBpip download\fP when acquiring a seed wheel so extras, pip flags, or shell metacharacters cannot be smuggled into the subprocess command line. (#3120 \%) .IP \(bu 2 Security hardening: replace the string\-prefix containment check in \fBvirtualenv.util.zipapp\fP with \fBPath.relative_to\fP so the zipapp extraction helpers refuse any path that does not resolve under the archive root. (#3121 \%) .IP \(bu 2 Security hardening: do not silently fall back to an unverified HTTPS context when the periodic update request to PyPI fails TLS verification. The returned metadata drives which wheel version virtualenv considers \(dqup to date\(dq, so accepting an unverified response lets a network\-level attacker suppress security updates. Set \fBVIRTUALENV_PERIODIC_UPDATE_INSECURE=1\fP to restore the previous behavior on hosts with broken trust stores. (#3122 \%) .UNINDENT .SS v21.2.3 (2026\-04\-14) .sp No significant changes. .SS v21.2.2 (2026\-04\-13) .SS Bugfixes \- 21.2.2 .INDENT 0.0 .IP \(bu 2 Bump \fBpython\-discovery\fP minimum to \fB>=1.2.2\fP to include \fBnormalize_isa\fP support \- by @rahuldevikar \%\&. (#3117 \%) .UNINDENT .SS v21.2.1 (2026\-04\-09) .SS Bugfixes \- 21.2.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB82.0.1\fP from \fB82.0.0\fP (#3093 \%) .UNINDENT .IP \(bu 2 Use terminal width for help formatting instead of hardcoded 240. (#3110 \%) .UNINDENT .SS v21.2.0 (2026\-03\-09) .SS Features \- 21.2.0 .INDENT 0.0 .IP \(bu 2 Update embed wheel generator (\fBtasks/upgrade_wheels.py\fP) to include type annotations in generated output \- by @rahuldevikar \%\&. (#3075 \%) .UNINDENT .SS Bugfixes \- 21.2.0 .INDENT 0.0 .IP \(bu 2 Pass \fB\-\-without\-scm\-ignore\-files\fP to subprocess venv on Python 3.13+ so virtualenv controls \fB\&.gitignore\fP creation, fixing flaky \fBtest_create_no_seed\fP and \fB\-\-no\-vcs\-ignore\fP being ignored in subprocess path \- by @gaborbernat \%\&. (#3089 \%) .IP \(bu 2 Use \fBBASH_SOURCE[0]\fP instead of \fB$0\fP in the bash activate script relocation fallback, fixing incorrect \fBPATH\fP when sourcing the activate script from a different directory \- by @gaborbernat \%\&. (#3090 \%) .UNINDENT .SS v21.1.0 (2026\-02\-27) .SS Features \- 21.1.0 .INDENT 0.0 .IP \(bu 2 Add comprehensive type annotations across the entire codebase and ship a PEP 561 \fBpy.typed\fP marker so downstream consumers and type checkers recognize virtualenv as an inline\-typed package \- by @rahuldevikar \%\&. (#3075 \%) .UNINDENT .SS v21.0.0 (2026\-02\-25) .SS Deprecations and Removals \- 21.0.0 .INDENT 0.0 .IP \(bu 2 The Python discovery logic has been extracted into a standalone \fBpython\-discovery\fP package on PyPI (documentation \%) and is now consumed as a dependency. If you previously imported discovery internals directly (e.g. \fBfrom virtualenv.discovery.py_info import PythonInfo\fP), switch to \fBfrom python_discovery import PythonInfo\fP\&. Backward\-compatibility re\-export shims are provided at \fBvirtualenv.discovery.py_info\fP, \fBvirtualenv.discovery.py_spec\fP, and \fBvirtualenv.discovery.cached_py_info\fP, however these are considered unsupported and may be removed in a future release \- by @gaborbernat \%\&. (#3070 \%) .UNINDENT .SS v20.39.1 (2026\-02\-25) .SS Features \- 20.39.1 .INDENT 0.0 .IP \(bu 2 Add support for creating virtual environments with RustPython \- by @elmjag \%\&. (#3010 \%) .UNINDENT .SS v20.39.0 (2026\-02\-23) .SS Features \- 20.39.0 .INDENT 0.0 .IP \(bu 2 Automatically resolve version manager shims (pyenv, mise, asdf) to the real Python binary during discovery, preventing incorrect interpreter selection when shims are on \fBPATH\fP \- by @gaborbernat \%\&. (#3049 \%) .IP \(bu 2 Add architecture (ISA) awareness to Python discovery — users can now specify a CPU architecture suffix in the \fB\-\-python\fP spec string (e.g. \fBcpython3.12\-64\-arm64\fP) to distinguish between interpreters that share the same version and bitness but target different architectures. Uses \fBsysconfig.get_platform()\fP as the data source, with cross\-platform normalization (\fBamd64\fP ↔ \fBx86_64\fP, \fBaarch64\fP ↔ \fBarm64\fP). Omitting the suffix preserves existing behavior \- by @rahuldevikar \%\&. (#3059 \%) .UNINDENT .SS v20.38.0 (2026\-02\-19) .SS Features \- 20.38.0 .INDENT 0.0 .IP \(bu 2 Store app data (pip/setuptools/wheel caches) under the OS cache directory (\fBplatformdirs.user_cache_dir\fP) instead of the data directory (\fBplatformdirs.user_data_dir\fP). Existing app data at the old location is automatically migrated on first use. This ensures cached files that can be redownloaded are placed in the standard cache location (e.g. \fB~/.cache\fP on Linux, \fB~/Library/Caches\fP on macOS) where they are excluded from backups and can be cleaned by system tools \- by @rahuldevikar \%\&. (#1884 \%) (#1884 \%) .IP \(bu 2 Add \fBPKG_CONFIG_PATH\fP environment variable support to all activation scripts (Bash, Batch, PowerShell, Fish, C Shell, Nushell, and Python). The virtualenv\(aqs \fBlib/pkgconfig\fP directory is now automatically prepended to \fBPKG_CONFIG_PATH\fP on activation and restored on deactivation, enabling packages that use \fBpkg\-config\fP during build/install to find their configuration files \- by @rahuldevikar \%\&. (#2637 \%) .IP \(bu 2 Upgrade embedded pip to \fB26.0.1\fP from \fB25.3\fP and setuptools to \fB82.0.0\fP, \fB75.3.4\fP from \fB75.3.2\fP, \fB80.9.0\fP \- by @rahuldevikar \%\&. (#3027 \%) .IP \(bu 2 Replace \fBty: ignore\fP comments with proper type narrowing using assertions and explicit None checks \- by @rahuldevikar \%\&. (#3029 \%) .UNINDENT .SS Bugfixes \- 20.38.0 .INDENT 0.0 .IP \(bu 2 Exclude pywin32 DLLs (\fBpywintypes*.dll\fP, \fBpythoncom*.dll\fP) from being copied to the Scripts directory during virtualenv creation on Windows. This fixes compatibility issues with pywin32, which expects its DLLs to be installed in \fBsite\-packages/pywin32_system32\fP by its own post\-install script \- by @rahuldevikar \%\&. (#2662 \%) .IP \(bu 2 Preserve symlinks in \fBpyvenv.cfg\fP paths to match \fBvenv\fP behavior. Use \fBos.path.abspath()\fP instead of \fBos.path.realpath()\fP to normalize paths without resolving symlinks, fixing issues with Python installations accessed via symlinked directories (common in network\-mounted filesystems) \- by @rahuldevikar \%\&. Fixes #2770 \%\&. (#2770 \%) .IP \(bu 2 Fix Windows activation scripts to properly quote \fBpython.exe\fP path, preventing failures when Python is installed in a path with spaces (e.g., \fBC:\eProgram Files\fP) and a file named \fBC:\eProgram\fP exists on the filesystem \- by @rahuldevikar \%\&. (#2985 \%) .IP \(bu 2 Fix \fBbash \-u\fP (\fBset \-o nounset\fP) compatibility in bash activation script by using \fB${PKG_CONFIG_PATH:\-}\fP and \fB${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}\fP to handle unset \fBPKG_CONFIG_PATH\fP \- by @Fridayai700 \%\&. (#3044 \%) .IP \(bu 2 Gracefully handle corrupted on\-disk cache and invalid JSON from Python interrogation subprocess instead of crashing with unhandled \fBJSONDecodeError\fP or \fBKeyError\fP \- by @gaborbernat \%\&. (#3054 \%) .UNINDENT .SS v20.36.1 (2026\-01\-09) .SS Bugfixes \- 20.36.1 .INDENT 0.0 .IP \(bu 2 Fix TOCTOU vulnerabilities in app_data and lock directory creation that could be exploited via symlink attacks \- reported by @tsigouris007 \%, fixed by @gaborbernat \%\&. (#3013 \%) .UNINDENT .SS v20.36.0 (2026\-01\-07) .SS Features \- 20.36.0 .INDENT 0.0 .IP \(bu 2 Add support for PEP 440 version specifiers in the \fB\-\-python\fP flag. Users can now specify Python versions using operators like \fB>=\fP, \fB<=\fP, \fB~=\fP, etc. For example: \fBvirtualenv \-\-python=\(dq>=3.12\(dq myenv\fP \fI\&. (:issue:\(ga2994\fP) .UNINDENT .SS v20.35.4 (2025\-10\-28) .SS Bugfixes \- 20.35.4 .INDENT 0.0 .IP \(bu 2 Fix race condition in \fB_virtualenv.py\fP when file is overwritten during import, preventing \fBNameError\fP when \fB_DISTUTILS_PATCH\fP is accessed \- by @gracetyy \%\&. (#2969 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB25.3\fP from \fB25.2\fP (#2989 \%) .UNINDENT .UNINDENT .SS v20.35.3 (2025\-10\-10) .SS Bugfixes \- 20.35.3 .INDENT 0.0 .IP \(bu 2 Accept RuntimeError in \fItest_too_many_open_files\fP, by @esafak \% (#2935 \%) .UNINDENT .SS v20.35.2 (2025\-10\-10) .SS Bugfixes \- 20.35.2 .INDENT 0.0 .IP \(bu 2 Revert out changes related to the extraction of the discovery module \- by @gaborbernat \%\&. (#2978 \%) .UNINDENT .SS v20.35.1 (2025\-10\-09) .SS Bugfixes \- 20.35.1 .INDENT 0.0 .IP \(bu 2 Patch get_interpreter to handle missing cache and app_data \- by @esafak \% (#2972 \%) .IP \(bu 2 Fix backwards incompatible changes to \fBPythonInfo\fP \- by @gaborbernat \%\&. (#2975 \%) .UNINDENT .SS v20.35.0 (2025\-10\-08) .SS Features \- 20.35.0 .INDENT 0.0 .IP \(bu 2 Add AppData and Cache protocols to discovery for decoupling \- by @esafak \%\&. (#2074 \%) .IP \(bu 2 Ensure python3.exe and python3 on Windows for Python 3 \- by @esafak \%\&. (#2774 \%) .UNINDENT .SS Bugfixes \- 20.35.0 .INDENT 0.0 .IP \(bu 2 Replaced direct references to tcl/tk library paths with getattr \- by @esafak \% (#2944 \%) .IP \(bu 2 Restore absolute import of fs_is_case_sensitive \- by @esafak \%\&. (#2955 \%) .UNINDENT .SS v20.34.0 (2025\-08\-13) .SS Features \- 20.34.0 .INDENT 0.0 .IP \(bu 2 Abstract out caching in discovery \- by @esafak \%\&. Decouple \fIFileCache\fP from \fIpy_info\fP (discovery) \- by @esafak \%\&. Remove references to py_info in FileCache \- by @esafak \%\&. Decouple discovery from creator plugins \- by @esafak \%\&. Decouple discovery by duplicating info utils \- by @esafak \%\&. (#2074 \%) .IP \(bu 2 Add PyPy 3.11 support. Contributed by @esafak \%\&. (#2932 \%) .UNINDENT .SS Bugfixes \- 20.34.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheel pip to \fB25.2\fP from \fB25.1.1\fP \- by @gaborbernat \%\&. (#2333 \%) .IP \(bu 2 Accept RuntimeError in \fItest_too_many_open_files\fP, by @esafak \% (#2935 \%) .IP \(bu 2 Python in PATH takes precedence over uv\-managed python. Contributed by @edgarrmondragon \%\&. (#2952 \%) .UNINDENT .SS v20.33.1 (2025\-08\-05) .SS Bugfixes \- 20.33.1 .INDENT 0.0 .IP \(bu 2 Correctly unpack _get_tcl_tk_libs() response in PythonInfo. Contributed by @esafak \%\&. (#2930 \%) .IP \(bu 2 Restore \fIpy_info.py\fP timestamp in \fItest_py_info_cache_invalidation_on_py_info_change\fP Contributed by @esafak \%\&. (#2933 \%) .UNINDENT .SS v20.33.0 (2025\-08\-03) .SS Features \- 20.33.0 .INDENT 0.0 .IP \(bu 2 Added support for Tcl and Tkinter. You\(aqre welcome. Contributed by @esafak \%\&. (#425 \%) .UNINDENT .SS Bugfixes \- 20.33.0 .INDENT 0.0 .IP \(bu 2 Prevent logging setup when \-\-help is passed, fixing a flaky test. Contributed by @esafak \%\&. (#u \%) .IP \(bu 2 Fix cache invalidation for PythonInfo by hashing \fIpy_info.py\fP\&. Contributed by @esafak \%\&. (#2467 \%) .IP \(bu 2 When no discovery plugins are found, the application would crash with a StopIteration. This change catches the StopIteration and raises a RuntimeError with a more informative message. Contributed by @esafak \%\&. (#2493 \%) .IP \(bu 2 Stop \fI\-\-try\-first\-with\fP overriding absolute \fI\-\-python\fP paths. Contributed by @esafak \%\&. (#2659 \%) .IP \(bu 2 Force UTF\-8 encoding for pip download Contributed by @esafak \%\&. (#2780 \%) .IP \(bu 2 Creating a virtual environment on a filesystem without symlink\-support would fail even with \fI\-\-copies\fP Make \fIfs_supports_symlink\fP perform a real symlink creation check on all platforms. Contributed by @esafak \%\&. (#2786 \%) .IP \(bu 2 Add a note to the user guide recommending the use of a specific Python version when creating virtual environments. Contributed by @esafak \%\&. (#2808 \%) .IP \(bu 2 Fix \(aqToo many open files\(aq error due to a file descriptor leak in virtualenv\(aqs locking mechanism. Contributed by @esafak \%\&. (#2834 \%) .IP \(bu 2 Support renamed Windows venv redirector (\fIvenvlauncher.exe\fP and \fIvenvwlauncher.exe\fP) on Python 3.13 Contributed by @esafak \%\&. (#2851 \%) .IP \(bu 2 Resolve Nushell activation script deprecation warnings by dynamically selecting the \fB\-\-optional\fP flag for Nushell \fBget\fP command on version 0.106.0 and newer, while retaining the deprecated \fB\-i\fP flag for older versions to maintain compatibility. Contributed by @gaborbernat \%\&. (#2910 \%) .UNINDENT .SS v20.32.0 (2025\-07\-20) .SS Features \- 20.32.0 .INDENT 0.0 .IP \(bu 2 Warn on incorrect invocation of Nushell activation script \- by @esafak \%\&. (#nushell_activation \%) .IP \(bu 2 Discover uv\-managed Python installations (#2901 \%) .UNINDENT .SS Bugfixes \- 20.32.0 .INDENT 0.0 .IP \(bu 2 Ignore missing absolute paths for python discovery \- by @esafak \% (#2870 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB80.9.0\fP from \fB80.3.1\fP \- by @gaborbernat \%\&. (#2900 \%) .UNINDENT .SS v20.31.2 (2025\-05\-08) .sp No significant changes. .SS v20.31.1 (2025\-05\-05) .SS Bugfixes \- 20.31.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB25.1.1\fP from \fB25.1\fP .IP \(bu 2 setuptools to \fB80.3.1\fP from \fB78.1.0\fP (#2880 \%) .UNINDENT .UNINDENT .SS v20.31.0 (2025\-05\-05) .SS Features \- 20.31.0 .INDENT 0.0 .IP \(bu 2 No longer bundle \fBwheel\fP wheels (except on Python 3.8), \fBsetuptools\fP includes native \fBbdist_wheel\fP support. Update \fBpip\fP to \fB25.1\fP\&. (#2868 \%) .UNINDENT .SS Bugfixes \- 20.31.0 .INDENT 0.0 .IP \(bu 2 \fBget_embed_wheel()\fP no longer fails with a \fBTypeError\fP \% when it is called with an unknown \fIdistribution\fP\&. (#2877 \%) .IP \(bu 2 Fix \fBHelpFormatter\fP error with Python 3.14.0b1. (#2878 \%) .UNINDENT .SS v20.30.0 (2025\-03\-31) .SS Features \- 20.30.0 .INDENT 0.0 .IP \(bu 2 Add support for GraalPy \%\&. (#2832 \%) .UNINDENT .SS Bugfixes \- 20.30.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB78.1.0\fP from \fB75.3.2\fP (#2863 \%) .UNINDENT .UNINDENT .SS v20.29.3 (2025\-03\-06) .SS Bugfixes \- 20.29.3 .INDENT 0.0 .IP \(bu 2 Ignore unreadable directories in \fBPATH\fP\&. (#2794 \%) .UNINDENT .SS v20.29.2 (2025\-02\-10) .SS Bugfixes \- 20.29.2 .INDENT 0.0 .IP \(bu 2 Remove old virtualenv wheel from the source distribution \- by @gaborbernat \%\&. (#2841 \%) .IP \(bu 2 Upgrade embedded wheel pip to \fB25.0.1\fP from \fB24.3.1\fP \- by @gaborbernat \%\&. (#2843 \%) .UNINDENT .SS v20.29.1 (2025\-01\-17) .SS Bugfixes \- 20.29.1 .INDENT 0.0 .IP \(bu 2 Fix PyInfo cache incompatibility warnings \- by @robsdedude \%\&. (#2827 \%) .UNINDENT .SS v20.29.0 (2025\-01\-15) .SS Features \- 20.29.0 .INDENT 0.0 .IP \(bu 2 Add support for selecting free\-threaded Python interpreters, e.g., \fIpython3.13t\fP\&. (#2809 \%) .UNINDENT .SS Bugfixes \- 20.29.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB75.8.0\fP from \fB75.6.0\fP (#2823 \%) .UNINDENT .UNINDENT .SS v20.28.1 (2025\-01\-02) .SS Bugfixes \- 20.28.1 .INDENT 0.0 .IP \(bu 2 Skip tcsh tests on broken tcsh versions \- by @gaborbernat \%\&. (#2814 \%) .UNINDENT .SS v20.28.0 (2024\-11\-25) .SS Features \- 20.28.0 .INDENT 0.0 .IP \(bu 2 Write CACHEDIR.TAG file on creation \- by \(dquser:\fIneilramsay\fP\&. (#2803 \%) .UNINDENT .SS v20.27.2 (2024\-11\-25) .SS Bugfixes \- 20.27.2 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB75.3.0\fP from \fB75.2.0\fP (#2798 \%) .UNINDENT .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 wheel to \fB0.45.0\fP from \fB0.44.0\fP .IP \(bu 2 setuptools to \fB75.5.0\fP (#2800 \%) .UNINDENT .IP \(bu 2 no longer forcibly echo off during windows batch activation (#2801 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB75.6.0\fP from \fB75.5.0\fP .IP \(bu 2 wheel to \fB0.45.1\fP from \fB0.45.0\fP (#2804 \%) .UNINDENT .UNINDENT .SS v20.27.1 (2024\-10\-28) .SS Bugfixes \- 20.27.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB24.3.1\fP from \fB24.2\fP (#2789 \%) .UNINDENT .UNINDENT .SS v20.27.0 (2024\-10\-17) .SS Features \- 20.27.0 .INDENT 0.0 .IP \(bu 2 Drop 3.7 support as the CI environments no longer allow it running \- by @gaborbernat \%\&. (#2758 \%) .UNINDENT .SS Bugfixes \- 20.27.0 .INDENT 0.0 .IP \(bu 2 When a \fB$PATH\fP entry cannot be checked for existence, skip it instead of terminating \- by @hroncok \%\&. (#2782 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB75.2.0\fP from \fB75.1.0\fP .IP \(bu 2 Removed pip of \fB24.0\fP .IP \(bu 2 Removed setuptools of \fB68.0.0\fP .IP \(bu 2 Removed wheel of \fB0.42.0\fP .IP \(bu 2 by @gaborbernat \%\&. (#2783 \%) .UNINDENT .IP \(bu 2 Fix zipapp is broken on Windows post distlib \fB0.3.9\fP \- by @gaborbernat \%\&. (#2784 \%) .UNINDENT .SS v20.26.6 (2024\-09\-27) .SS Bugfixes \- 20.26.6 .INDENT 0.0 .IP \(bu 2 Properly quote string placeholders in activation script templates to mitigate potential command injection \- by @y5c4l3 \%\&. (#2768 \%) .UNINDENT .SS v20.26.5 (2024\-09\-17) .SS Bugfixes \- 20.26.5 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: setuptools to \fB75.1.0\fP from \fB74.1.2\fP \- by @gaborbernat \%\&. (#2765 \%) .UNINDENT .SS v20.26.4 (2024\-09\-07) .SS Bugfixes \- 20.26.4 .INDENT 0.0 .IP \(bu 2 no longer create \fI()\fP output in console during activation of a virtualenv by .bat file. (#2728 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 wheel to \fB0.44.0\fP from \fB0.43.0\fP .IP \(bu 2 pip to \fB24.2\fP from \fB24.1\fP .IP \(bu 2 setuptools to \fB74.1.2\fP from \fB70.1.0\fP (#2760 \%) .UNINDENT .UNINDENT .SS v20.26.3 (2024\-06\-21) .SS Bugfixes \- 20.26.3 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB70.1.0\fP from \fB69.5.1\fP .IP \(bu 2 pip to \fB24.1\fP from \fB24.0\fP (#2741 \%) .UNINDENT .UNINDENT .SS v20.26.2 (2024\-05\-13) .SS Bugfixes \- 20.26.2 .INDENT 0.0 .IP \(bu 2 \fBvirtualenv.pyz\fP no longer fails when zipapp path contains a symlink \- by @HandSonic \% and @petamas \%\&. (#1949 \%) .IP \(bu 2 Fix bad return code from activate.sh if hashing is disabled \- by :user:\(aqfenkes\-ibm\(aq. (#2717 \%) .UNINDENT .SS v20.26.1 (2024\-04\-29) .SS Bugfixes \- 20.26.1 .INDENT 0.0 .IP \(bu 2 fix PATH\-based Python discovery on Windows \- by @ofek \%\&. (#2712 \%) .UNINDENT .SS v20.26.0 (2024\-04\-23) .SS Bugfixes \- 20.26.0 .INDENT 0.0 .IP \(bu 2 allow builtin discovery to discover specific interpreters (e.g. \fBpython3.12\fP) given an unspecific spec (e.g. \fBpython3\fP) \- by @flying\-sheep \%\&. (#2709 \%) .UNINDENT .SS v20.25.3 (2024\-04\-17) .SS Bugfixes \- 20.25.3 .INDENT 0.0 .IP \(bu 2 Python 3.13.0a6 renamed pathmod to parser. (#2702 \%) .UNINDENT .SS v20.25.2 (2024\-04\-16) .SS Bugfixes \- 20.25.2 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools of \fB69.1.0\fP to \fB69.5.1\fP .IP \(bu 2 wheel of \fB0.42.0\fP to \fB0.43.0\fP (#2699 \%) .UNINDENT .UNINDENT .SS v20.25.1 (2024\-02\-21) .SS Bugfixes \- 20.25.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB69.0.3\fP from \fB69.0.2\fP .IP \(bu 2 pip to \fB23.3.2\fP from \fB23.3.1\fP (#2681 \%) .UNINDENT .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip \fB23.3.2\fP to \fB24.0\fP, .IP \(bu 2 setuptools \fB69.0.3\fP to \fB69.1.0\fP\&. (#2691 \%) .UNINDENT .UNINDENT .SS Misc \- 20.25.1 .INDENT 0.0 .IP \(bu 2 #2688 \% .UNINDENT .SS v20.25.0 (2023\-12\-01) .SS Features \- 20.25.0 .INDENT 0.0 .IP \(bu 2 The tests now pass on the CI with Python 3.13.0a2 \- by @hroncok \%\&. (#2673 \%) .UNINDENT .SS Bugfixes \- 20.25.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 wheel to \fB0.41.3\fP from \fB0.41.2\fP (#2665 \%) .UNINDENT .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 wheel to \fB0.42.0\fP from \fB0.41.3\fP .IP \(bu 2 setuptools to \fB69.0.2\fP from \fB68.2.2\fP (#2669 \%) .UNINDENT .UNINDENT .SS v20.24.6 (2023\-10\-23) .SS Bugfixes \- 20.24.6 .INDENT 0.0 .IP \(bu 2 Use get_hookimpls method instead of the private attribute in tests. (#2649 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB68.2.2\fP from \fB68.2.0\fP .IP \(bu 2 pip to \fB23.3.1\fP from \fB23.2.1\fP (#2656 \%) .UNINDENT .UNINDENT .SS v20.24.5 (2023\-09\-08) .SS Bugfixes \- 20.24.5 .INDENT 0.0 .IP \(bu 2 Declare PyPy 3.10 support \- by @cclauss \%\&. (#2638 \%) .IP \(bu 2 Brew on macOS no longer allows copy builds \- disallow choosing this by @gaborbernat \%\&. (#2640 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB68.2.0\fP from \fB68.1.2\fP (#2642 \%) .UNINDENT .UNINDENT .SS v20.24.4 (2023\-08\-30) .SS Bugfixes \- 20.24.4 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB68.1.2\fP from \fB68.1.0\fP on \fB3.8+\fP .IP \(bu 2 wheel to \fB0.41.2\fP from \fB0.41.1\fP on \fB3.7+\fP (#2628 \%) .UNINDENT .UNINDENT .SS v20.24.3 (2023\-08\-11) .SS Bugfixes \- 20.24.3 .INDENT 0.0 .IP \(bu 2 Fixed ResourceWarning on exit caused by periodic update subprocess (#2472 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 wheel to \fB0.41.1\fP from \fB0.41.0\fP (#2622 \%) .UNINDENT .UNINDENT .SS Misc \- 20.24.3 .INDENT 0.0 .IP \(bu 2 #2610 \% .UNINDENT .SS v20.24.2 (2023\-07\-24) .SS Bugfixes \- 20.24.2 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB23.2.1\fP from \fB23.2\fP .IP \(bu 2 wheel to \fB0.41.0\fP from \fB0.40.0\fP (#2614 \%) .UNINDENT .UNINDENT .SS v20.24.1 (2023\-07\-19) .SS Bugfixes \- 20.24.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 pip to \fB23.2\fP from \fB23.1.2\fP \- by @arielkirkwood \% (#2611 \%) .UNINDENT .UNINDENT .SS v20.24.0 (2023\-07\-14) .SS Features \- 20.24.0 .INDENT 0.0 .IP \(bu 2 Export the prompt prefix as \fBVIRTUAL_ENV_PROMPT\fP when activating a virtual environment \- by @jimporter \%\&. (#2194 \%) .UNINDENT .SS Bugfixes \- 20.24.0 .INDENT 0.0 .IP \(bu 2 Fix test suite \- by @gaborbernat \%\&. (#2592 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB68.0.0\fP from \fB67.8.0\fP (#2607 \%) .UNINDENT .UNINDENT .SS v20.23.1 (2023\-06\-16) .SS Bugfixes \- 20.23.1 .INDENT 0.0 .IP \(bu 2 update and simplify nushell activation script, fixes an issue on Windows resulting in consecutive command not found \- by @melMass \%\&. (#2572 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 setuptools to \fB67.8.0\fP from \fB67.7.2\fP (#2588 \%) .UNINDENT .UNINDENT .SS v20.23.0 (2023\-04\-27) .SS Features \- 20.23.0 .INDENT 0.0 .IP \(bu 2 Do not install \fBwheel\fP and \fBsetuptools\fP seed packages for Python 3.12+. To restore the old behavior use: .INDENT 2.0 .IP \(bu 2 for \fBwheel\fP use \fBVIRTUALENV_WHEEL=bundle\fP environment variable or \fB\-\-wheel=bundle\fP CLI flag, .IP \(bu 2 for \fBsetuptools\fP use \fBVIRTUALENV_SETUPTOOLS=bundle\fP environment variable or \fB\-\-setuptools=bundle\fP CLI flag. .UNINDENT .sp By @chrysle \%\&. (#2487 \%) .IP \(bu 2 3.12 support \- by @gaborbernat \%\&. (#2558 \%) .UNINDENT .SS Bugfixes \- 20.23.0 .INDENT 0.0 .IP \(bu 2 Prevent \fBPermissionError\fP when using venv creator on systems that deliver files without user write permission \- by @kulikjak \%\&. (#2543 \%) .IP \(bu 2 Upgrade setuptools to \fB67.7.2\fP from \fB67.6.1\fP and pip to \fB23.1.2\fP from \fB23.1\fP \- by @szleb \%\&. (#2560 \%) .UNINDENT .SS v20.22.0 (2023\-04\-19) .SS Features \- 20.22.0 .INDENT 0.0 .IP \(bu 2 Drop support for creating Python <=3.6 (including 2) interpreters. Removed pip of \fB20.3.4\fP, \fB21.3.1\fP; wheel of \fB0.37.1\fP; setuptools of \fB59.6.0\fP, \fB44.1.1\fP, \fB50.3.2\fP\- by @gaborbernat \%\&. (#2548 \%) .UNINDENT .SS v20.21.1 (2023\-04\-19) .SS Bugfixes \- 20.21.1 .INDENT 0.0 .IP \(bu 2 Add \fBtox.ini\fP to sdist \- by @mtelka \%\&. (#2511 \%) .IP \(bu 2 Move the use of \(aqlet\(aq in nushell to ensure compatibility with future releases of nushell, where \(aqlet\(aq no longer assumes that its initializer is a full expressions. (#2527 \%) .IP \(bu 2 The nushell command \(aqstr collect\(aq has been superseded by the \(aqstr join\(aq command. The activate.nu script has been updated to reflect this change. (#2532 \%) .IP \(bu 2 Upgrade embedded wheels: .INDENT 2.0 .IP \(bu 2 wheel to \fB0.40.0\fP from \fB0.38.4\fP .IP \(bu 2 setuptools to \fB67.6.1\fP from \fB67.4.0\fP .IP \(bu 2 pip to \fB23.1\fP from \fB23.0.1\fP (#2546 \%) .UNINDENT .UNINDENT .SS v20.21.0 (2023\-03\-12) .SS Features \- 20.21.0 .INDENT 0.0 .IP \(bu 2 Make closure syntax explicitly starts with {||. (#2512 \%) .UNINDENT .SS Bugfixes \- 20.21.0 .INDENT 0.0 .IP \(bu 2 Add \fBprint\fP command to nushell print_prompt to ensure compatibility with future release of nushell, where intermediate commands no longer print their result to stdout. (#2514 \%) .IP \(bu 2 Do not assume the default encoding. (#2515 \%) .IP \(bu 2 Make \fBReentrantFileLock\fP thread\-safe and, thereby, fix race condition in \fBvirtualenv.cli_run\fP \- by @radoering \%\&. (#2516 \%) .UNINDENT .SS v20.20.0 (2023\-02\-28) .SS Features \- 20.20.0 .INDENT 0.0 .IP \(bu 2 Change environment variable existence check in Nushell activation script to not use deprecated command. (#2506 \%) .UNINDENT .SS Bugfixes \- 20.20.0 .INDENT 0.0 .IP \(bu 2 Discover CPython implementations distributed on Windows by any organization \- by @faph \%\&. (#2504 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB67.4.0\fP from \fB67.1.0\fP and pip to \fB23.0.1\fP from \fB23.0\fP \- by @gaborbernat \%\&. (#2510 \%) .UNINDENT .SS v20.19.0 (2023\-02\-07) .SS Features \- 20.19.0 .INDENT 0.0 .IP \(bu 2 Allow platformdirs version \fB3\fP \- by @cdce8p \%\&. (#2499 \%) .UNINDENT .SS v20.18.0 (2023\-02\-06) .SS Features \- 20.18.0 .INDENT 0.0 .IP \(bu 2 Drop \fB3.6\fP runtime support (can still create \fB2.7+\fP) \- by @gaborbernat \%\&. (#2489 \%) .UNINDENT .SS Bugfixes \- 20.18.0 .INDENT 0.0 .IP \(bu 2 Fix broken prompt in Nushell when activating virtual environment \- by @kubouc \%\&. (#2481 \%) .IP \(bu 2 Bump embedded pip to \fB23.0\fP and setuptools to \fB67.1\fP \- by @gaborbernat \%\&. (#2489 \%) .UNINDENT .SS v20.17.1 (2022\-12\-05) .SS Bugfixes \- 20.17.1 .INDENT 0.0 .IP \(bu 2 A \fBpy\fP or \fBpython\fP spec means any Python rather than \fBCPython\fP \- by @gaborbernat \%\&. (#2460 \%) .IP \(bu 2 Make \fBactivate.nu\fP respect \fBVIRTUAL_ENV_DISABLE_PROMPT\fP and not set the prompt if requested \- by @m\-lima \%\&. (#2461 \%) .UNINDENT .SS v20.17.0 (2022\-11\-27) .SS Features \- 20.17.0 .INDENT 0.0 .IP \(bu 2 Change Nushell activation script to be a module meant to be activated as an overlay. (#2422 \%) .IP \(bu 2 Update operator used in Nushell activation script to be compatible with future versions. (#2450 \%) .UNINDENT .SS Bugfixes \- 20.17.0 .INDENT 0.0 .IP \(bu 2 Do not use deprecated API from \fBimportlib.resources\fP on Python 3.10 or later \- by @gaborbernat \%\&. (#2448 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB65.6.3\fP from \fB65.5.1\fP \- by @gaborbernat \%\&. (#2451 \%) .UNINDENT .SS v20.16.7 (2022\-11\-12) .SS Bugfixes \- 20.16.7 .INDENT 0.0 .IP \(bu 2 Use parent directory of python executable for pyvenv.cfg \(dqhome\(dq value per PEP 405 \- by @vfazio \%\&. (#2440 \%) .IP \(bu 2 In POSIX virtual environments, try alternate binary names if \fBsys._base_executable\fP does not exist \- by @vfazio \%\&. (#2442 \%) .IP \(bu 2 Upgrade embedded wheel to \fB0.38.4\fP and pip to \fB22.3.1\fP from \fB22.3\fP and setuptools to \fB65.5.1\fP from \fB65.5.0\fP \- by @gaborbernat \%\&. (#2443 \%) .UNINDENT .SS v20.16.6 (2022\-10\-25) .SS Features \- 20.16.6 .INDENT 0.0 .IP \(bu 2 Drop unneeded shims for PyPy3 directory structure (#2426 \%) .UNINDENT .SS Bugfixes \- 20.16.6 .INDENT 0.0 .IP \(bu 2 Fix selected scheme on debian derivatives for python 3.10 when \fBpython3\-distutils\fP is not installed or the \fBvenv\fP scheme is not available \- by @asottile \%\&. (#2350 \%) .IP \(bu 2 Allow the test suite to pass even with the original C shell (rather than \fBtcsh\fP) \- by @kulikjak \%\&. (#2418 \%) .IP \(bu 2 Fix fallback handling of downloading wheels for bundled packages \- by @schaap \%\&. (#2429 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB65.5.0\fP from \fB65.3.0\fP and pip to \fB22.3\fP from \fB22.2.2\fP \- by @gaborbernat \%\&. (#2434 \%) .UNINDENT .SS v20.16.5 (2022\-09\-07) .SS Bugfixes \- 20.16.5 .INDENT 0.0 .IP \(bu 2 Do not turn echo off for subsequent commands in batch activators (\fBactivate.bat\fP and \fBdeactivate.bat\fP) \- by @pawelszramowski \%\&. (#2411 \%) .UNINDENT .SS v20.16.4 (2022\-08\-29) .SS Bugfixes \- 20.16.4 .INDENT 0.0 .IP \(bu 2 Bump embed setuptools to \fB65.3\fP \- by @gaborbernat \%\&. (#2405 \%) .UNINDENT .SS v20.16.3 (2022\-08\-04) .SS Bugfixes \- 20.16.3 .INDENT 0.0 .IP \(bu 2 Upgrade embedded pip to \fB22.2.2\fP from \fB22.2.1\fP and setuptools to \fB63.4.1\fP from \fB63.2.0\fP \- by @gaborbernat \%\&. (#2395 \%) .UNINDENT .SS v20.16.2 (2022\-07\-27) .SS Bugfixes \- 20.16.2 .INDENT 0.0 .IP \(bu 2 Bump embedded pip from \fB22.2\fP to \fB22.2.1\fP \- by @gaborbernat \%\&. (#2391 \%) .UNINDENT .SS v20.16.1 (2022\-07\-26) .SS Features \- 20.16.1 .INDENT 0.0 .IP \(bu 2 Update Nushell activation scripts to version 0.67 \- by @kubouch \%\&. (#2386 \%) .UNINDENT .SS v20.16.0 (2022\-07\-25) .SS Features \- 20.16.0 .INDENT 0.0 .IP \(bu 2 Drop support for running under Python 2 (still can generate Python 2 environments) \- by @gaborbernat \%\&. (#2382 \%) .IP \(bu 2 Upgrade embedded pip to \fB22.2\fP from \fB22.1.2\fP and setuptools to \fB63.2.0\fP from \fB62.6.0\fP \- by @gaborbernat \%\&. (#2383 \%) .UNINDENT .SS v20.15.1 (2022\-06\-28) .SS Bugfixes \- 20.15.1 .INDENT 0.0 .IP \(bu 2 Fix the incorrect operation when \fBsetuptools\fP plugins output something into \fBstdout\fP\&. (#2335 \%) .IP \(bu 2 CPython3Windows creator ignores missing \fBDLLs\fP dir. (#2368 \%) .UNINDENT .SS v20.15.0 (2022\-06\-25) .SS Features \- 20.15.0 .INDENT 0.0 .IP \(bu 2 Support for Windows embeddable Python package: includes \fBpython.zip\fP in the creator sources \- by @reksarka \%\&. (#1774 \%) .UNINDENT .SS Bugfixes \- 20.15.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded setuptools to \fB62.3.3\fP from \fB62.6.0\fP and pip to \fB22.1.2\fP from \fB22.0.4\fP \- by @gaborbernat \%\&. (#2348 \%) .IP \(bu 2 Use \fBshlex.quote\fP instead of deprecated \fBpipes.quote\fP in Python 3 \- by @frenzymadness \%\&. (#2351 \%) .IP \(bu 2 Fix Windows PyPy 3.6 \- by @reksarka \%\&. (#2363 \%) .UNINDENT .SS v20.14.1 (2022\-04\-11) .SS Features \- 20.14.1 .INDENT 0.0 .IP \(bu 2 Support for creating a virtual environment from a Python 2.7 framework on macOS 12 \- by @nickhutchinson \%\&. (#2284 \%) .UNINDENT .SS Bugfixes \- 20.14.1 .INDENT 0.0 .IP \(bu 2 Upgrade embedded setuptools to \fB62.1.0\fP from \fB61.0.0\fP \- by @gaborbernat \%\&. (#2327 \%) .UNINDENT .SS v20.14.0 (2022\-03\-25) .SS Features \- 20.14.0 .INDENT 0.0 .IP \(bu 2 Support Nushell activation scripts with nu version \fB0.60\fP \- by @kubouch \%\&. (#2321 \%) .UNINDENT .SS Bugfixes \- 20.14.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded setuptools to \fB61.0.0\fP from \fB60.10.0\fP \- by @gaborbernat \%\&. (#2322 \%) .UNINDENT .SS v20.13.4 (2022\-03\-18) .SS Bugfixes \- 20.13.4 .INDENT 0.0 .IP \(bu 2 Improve performance of python startup inside created virtualenvs \- by @asottile \%\&. (#2317 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB60.10.0\fP from \fB60.9.3\fP \- by @gaborbernat \%\&. (#2320 \%) .UNINDENT .SS v20.13.3 (2022\-03\-07) .SS Bugfixes \- 20.13.3 .INDENT 0.0 .IP \(bu 2 Avoid symlinking the contents of \fB/usr\fP into PyPy3.8+ virtualenvs \- by @stefanor \%\&. (#2310 \%) .IP \(bu 2 Bump embed pip from \fB22.0.3\fP to \fB22.0.4\fP \- by @gaborbernat \%\&. (#2311 \%) .UNINDENT .SS v20.13.2 (2022\-02\-24) .SS Bugfixes \- 20.13.2 .INDENT 0.0 .IP \(bu 2 Upgrade embedded setuptools to \fB60.9.3\fP from \fB60.6.0\fP \- by @gaborbernat \%\&. (#2306 \%) .UNINDENT .SS v20.13.1 (2022\-02\-05) .SS Bugfixes \- 20.13.1 .INDENT 0.0 .IP \(bu 2 fix \(dqexecv() arg 2 must contain only strings\(dq error on M1 MacOS (#2282 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB60.5.0\fP from \fB60.2.0\fP \- by @asottile \%\&. (#2289 \%) .IP \(bu 2 Upgrade embedded pip to \fB22.0.3\fP and setuptools to \fB60.6.0\fP \- by @gaborbernat \% and @asottile \%\&. (#2294 \%) .UNINDENT .SS v20.13.0 (2022\-01\-02) .SS Features \- 20.13.0 .INDENT 0.0 .IP \(bu 2 Add downloaded wheel information in the relevant JSON embed file to prevent additional downloads of the same wheel. \- by @mayeut \%\&. (#2268 \%) .UNINDENT .SS Bugfixes \- 20.13.0 .INDENT 0.0 .IP \(bu 2 Fix \fBAttributeError: \(aqbool\(aq object has no attribute \(aqerror\(aq\fP when creating a Python 2.x virtualenv on macOS \- by \fBmoreati\fP\&. (#2269 \%) .IP \(bu 2 Fix \fBPermissionError: [Errno 1] Operation not permitted\fP when creating a Python 2.x virtualenv on macOS/arm64 \- by \fBmoreati\fP\&. (#2271 \%) .UNINDENT .SS v20.12.1 (2022\-01\-01) .SS Bugfixes \- 20.12.1 .INDENT 0.0 .IP \(bu 2 Try using previous updates of \fBpip\fP, \fBsetuptools\fP & \fBwheel\fP when inside an update grace period rather than always falling back to embedded wheels \- by @mayeut \%\&. (#2265 \%) .IP \(bu 2 New patch versions of \fBpip\fP, \fBsetuptools\fP & \fBwheel\fP are now returned in the expected timeframe. \- by @mayeut \%\&. (#2266 \%) .IP \(bu 2 Manual upgrades of \fBpip\fP, \fBsetuptools\fP & \fBwheel\fP are not discarded by a periodic update \- by @mayeut \%\&. (#2267 \%) .UNINDENT .SS v20.12.0 (2021\-12\-31) .SS Features \- 20.12.0 .INDENT 0.0 .IP \(bu 2 Sign the python2 exe on Darwin arm64 \- by @tmspicer \%\&. (#2233 \%) .UNINDENT .SS Bugfixes \- 20.12.0 .INDENT 0.0 .IP \(bu 2 Fix \fB\-\-download\fP option \- by @mayeut \%\&. (#2120 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB60.2.0\fP from \fB60.1.1\fP \- by @gaborbernat \%\&. (#2263 \%) .UNINDENT .SS v20.11.2 (2021\-12\-29) .SS Bugfixes \- 20.11.2 .INDENT 0.0 .IP \(bu 2 Fix installation of pinned versions of \fBpip\fP, \fBsetuptools\fP & \fBwheel\fP \- by @mayeut \%\&. (#2203 \%) .UNINDENT .SS v20.11.1 (2021\-12\-29) .SS Bugfixes \- 20.11.1 .INDENT 0.0 .IP \(bu 2 Bump embed setuptools to \fB60.1.1\fP from \fB60.1.0\fP \- by @gaborbernat \%\&. (#2258 \%) .UNINDENT .SS v20.11.0 (2021\-12\-28) .SS Features \- 20.11.0 .INDENT 0.0 .IP \(bu 2 Avoid deprecation warning from py\-filelock argument \- by @ofek \%\&. (#2237 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB61.1.0\fP from \fB58.3.0\fP \- by @gaborbernat \%\&. (#2240 \%) .IP \(bu 2 Drop the runtime dependency of \fBbackports.entry\-points\-selectable\fP \- by @hroncok \%\&. (#2246 \%) .IP \(bu 2 Fish: PATH variables should not be quoted when being set \- by @d3dave \%\&. (#2248 \%) .UNINDENT .SS v20.10.0 (2021\-11\-01) .SS Features \- 20.10.0 .INDENT 0.0 .IP \(bu 2 If a \fB\(dqvenv\(dq\fP install scheme exists in \fBsysconfig\fP, virtualenv now uses it to create new virtual environments. This allows Python distributors, such as Fedora, to patch/replace the default install scheme without affecting the paths in new virtual environments. A similar technique was proposed to Python, for the venv module \% \- by \fBhroncok\fP (#2208 \%) .IP \(bu 2 The activated virtualenv prompt is now always wrapped in parentheses. This affects venvs created with the \fB\-\-prompt\fP attribute, and matches virtualenv\(aqs behavior on par with venv. (#2224 \%) .UNINDENT .SS Bugfixes \- 20.10.0 .INDENT 0.0 .IP \(bu 2 Fix broken prompt set up by activate.bat \- by @SiggyBar \%\&. (#2225 \%) .UNINDENT .SS v20.9.0 (2021\-10\-23) .SS Features \- 20.9.0 .INDENT 0.0 .IP \(bu 2 Special\-case \fB\-\-prompt .\fP to the name of the current directory \- by @rkm \%\&. (#2220 \%) .IP \(bu 2 Add libffi\-8.dll to pypy windows #2218 \% \- by @mattip \% .UNINDENT .SS Bugfixes \- 20.9.0 .INDENT 0.0 .IP \(bu 2 Fixed path collision that could lead to a PermissionError or writing to system directories when using PyPy3.8 \- by @mgorny \%\&. (#2182 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB58.3.0\fP from \fB58.1.0\fP and pip to \fB21.3.1\fP from \fB21.2.4\fP \- by @gaborbernat \%\&. (#2205 \%) .IP \(bu 2 Remove stray closing parenthesis in activate.bat \- by @SiggyBar \%\&. (#2221 \%) .UNINDENT .SS v20.8.1 (2021\-09\-24) .SS Bugfixes \- 20.8.1 .INDENT 0.0 .IP \(bu 2 Fixed a bug where while creating a venv on top of an existing one, without cleaning, when seeded wheel version mismatch occurred, multiple \fB\&.dist\-info\fP directories may be present, confounding entrypoint discovery \- by @arcivanov \% (#2185 \%) .IP \(bu 2 Bump embed setuptools from \fB58.0.4\fP to \fB58.1.0\fP \- by @gaborbernat \%\&. (#2195 \%) .UNINDENT .SS Misc \- 20.8.1 .INDENT 0.0 .IP \(bu 2 #2189 \% .UNINDENT .SS v20.8.0 (2021\-09\-16) .INDENT 0.0 .IP \(bu 2 upgrade embedded setuptools to \fB58.0.4\fP from \fB57.4.0\fP and pip to \fB21.2.4\fP from \fB21.2.3\fP .IP \(bu 2 Add nushell activation script .UNINDENT .SS v20.7.2 (2021\-08\-10) .SS Bugfixes \- 20.7.2 .INDENT 0.0 .IP \(bu 2 Upgrade embedded pip to \fB21.2.3\fP from \fB21.2.2\fP and wheel to \fB0.37.0\fP from \fB0.36.2\fP \- by @gaborbernat \%\&. (#2168 \%) .UNINDENT .SS v20.7.1 (2021\-08\-09) .SS Bugfixes \- 20.7.1 .INDENT 0.0 .IP \(bu 2 Fix unpacking dictionary items in PythonInfo.install_path (#2165 \%) .UNINDENT .SS v20.7.0 (2021\-07\-31) .SS Bugfixes \- 20.7.0 .INDENT 0.0 .IP \(bu 2 upgrade embedded pip to \fB21.2.2\fP from \fB21.1.3\fP and setuptools to \fB57.4.0\fP from \fB57.1.0\fP \- by @gaborbernat \% (#2159 \%) .UNINDENT .SS Deprecations and Removals \- 20.7.0 .INDENT 0.0 .IP \(bu 2 Removed \fBxonsh\fP activator due to this breaking fairly often the CI and lack of support from those packages maintainers, upstream is encouraged to continue supporting the project as a plugin \% \- by @gaborbernat \%\&. (#2160 \%) .UNINDENT .SS v20.6.0 (2021\-07\-14) .SS Features \- 20.6.0 .INDENT 0.0 .IP \(bu 2 Support Python interpreters without \fBdistutils\fP (fallback to \fBsyconfig\fP in these cases) \- by @gaborbernat \%\&. (#1910 \%) .UNINDENT .SS v20.5.0 (2021\-07\-13) .SS Features \- 20.5.0 .INDENT 0.0 .IP \(bu 2 Plugins now use \(aqselectable\(aq entry points \- by @jaraco \%\&. (#2093 \%) .IP \(bu 2 add libffi\-7.dll to the hard\-coded list of dlls for PyPy (#2141 \%) .IP \(bu 2 Use the better maintained \fBplatformdirs\fP instead of \fBappdirs\fP \- by @gaborbernat \%\&. (#2142 \%) .UNINDENT .SS Bugfixes \- 20.5.0 .INDENT 0.0 .IP \(bu 2 Bump pip the embedded pip \fB21.1.3\fP and setuptools to \fB57.1.0\fP \- by @gaborbernat \%\&. (#2135 \%) .UNINDENT .SS Deprecations and Removals \- 20.5.0 .INDENT 0.0 .IP \(bu 2 Drop python \fB3.4\fP support as it has been over 2 years since EOL \- by @gaborbernat \%\&. (#2141 \%) .UNINDENT .SS v20.4.7 (2021\-05\-24) .SS Bugfixes \- 20.4.7 .INDENT 0.0 .IP \(bu 2 Upgrade embedded pip to \fB21.1.2\fP and setuptools to \fB57.0.0\fP \- by @gaborbernat \%\&. (#2123 \%) .UNINDENT .SS v20.4.6 (2021\-05\-05) .SS Bugfixes \- 20.4.6 .INDENT 0.0 .IP \(bu 2 Fix \fBsite.getsitepackages()\fP broken on python2 on debian \- by @freundTech \%\&. (#2105 \%) .UNINDENT .SS v20.4.5 (2021\-05\-05) .SS Bugfixes \- 20.4.5 .INDENT 0.0 .IP \(bu 2 Bump pip to \fB21.1.1\fP from \fB21.0.1\fP \- by @gaborbernat \%\&. (#2104 \%) .IP \(bu 2 Fix \fBsite.getsitepackages()\fP ignoring \fB\-\-system\-site\-packages\fP on python2 \- by @freundTech \%\&. (#2106 \%) .UNINDENT .SS v20.4.4 (2021\-04\-20) .SS Bugfixes \- 20.4.4 .INDENT 0.0 .IP \(bu 2 Built in discovery class is always preferred over plugin supplied classes. (#2087 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB56.0.0\fP by @gaborbernat \%\&. (#2094 \%) .UNINDENT .SS v20.4.3 (2021\-03\-16) .SS Bugfixes \- 20.4.3 .INDENT 0.0 .IP \(bu 2 Bump embedded setuptools from \fB52.0.0\fP to \fB54.1.2\fP \- by @gaborbernat \% (#2069 \%) .IP \(bu 2 Fix PyPy3 stdlib on Windows is incorrect \- by @gaborbernat \%\&. (#2071 \%) .UNINDENT .SS v20.4.2 (2021\-02\-01) .SS Bugfixes \- 20.4.2 .INDENT 0.0 .IP \(bu 2 Running virtualenv \fB\-\-upgrade\-embed\-wheels\fP crashes \- by @gaborbernat \%\&. (#2058 \%) .UNINDENT .SS v20.4.1 (2021\-01\-31) .SS Bugfixes \- 20.4.1 .INDENT 0.0 .IP \(bu 2 Bump embedded pip and setuptools packages to latest upstream supported (\fB21.0.1\fP and \fB52.0.0\fP) \- by @gaborbernat \%\&. (#2060 \%) .UNINDENT .SS v20.4.0 (2021\-01\-19) .SS Features \- 20.4.0 .INDENT 0.0 .IP \(bu 2 On the programmatic API allow passing in the environment variable dictionary to use, defaults to \fBos.environ\fP if not specified \- by @gaborbernat \%\&. (#2054 \%) .UNINDENT .SS Bugfixes \- 20.4.0 .INDENT 0.0 .IP \(bu 2 Upgrade embedded setuptools to \fB51.3.3\fP from \fB51.1.2\fP \- by @gaborbernat \%\&. (#2055 \%) .UNINDENT .SS v20.3.1 (2021\-01\-13) .SS Bugfixes \- 20.3.1 .INDENT 0.0 .IP \(bu 2 Bump embed pip to \fB20.3.3\fP, setuptools to \fB51.1.1\fP and wheel to \fB0.36.2\fP \- by @gaborbernat \%\&. (#2036 \%) .IP \(bu 2 Allow unfunctioning of pydoc to fail freely so that virtualenvs can be activated under Zsh with set \-e (since otherwise \fBunset \-f\fP and \fBunfunction\fP exit with 1 if the function does not exist in Zsh) \- by @d125q \%\&. (#2049 \%) .IP \(bu 2 Drop cached python information if the system executable is no longer present (for example when the executable is a shim and the mapped executable is replaced \- such is the case with pyenv) \- by @gaborbernat \%\&. (#2050 \%) .UNINDENT .SS v20.3.0 (2021\-01\-10) .SS Features \- 20.3.0 .INDENT 0.0 .IP \(bu 2 The builtin discovery takes now a \fB\-\-try\-first\-with\fP argument and is first attempted as valid interpreters. One can use this to force discovery of a given python executable when the discovery order/mechanism raises errors \- by @gaborbernat \%\&. (#2046 \%) .UNINDENT .SS Bugfixes \- 20.3.0 .INDENT 0.0 .IP \(bu 2 On Windows python \fB3.7+\fP distributions where the exe shim is missing fallback to the old ways \- by @gaborbernat \%\&. (#1986 \%) .IP \(bu 2 When discovering interpreters on Windows, via the PEP\-514, prefer \fBPythonCore\fP releases over other ones. virtualenv is used via pip mostly by this distribution, so prefer it over other such as conda \- by @gaborbernat \%\&. (#2046 \%) .UNINDENT .SS v20.2.2 (2020\-12\-07) .SS Bugfixes \- 20.2.2 .INDENT 0.0 .IP \(bu 2 Bump pip to \fB20.3.1\fP, setuptools to \fB51.0.0\fP and wheel to \fB0.36.1\fP \- by @gaborbernat \%\&. (#2029 \%) .UNINDENT .SS v20.2.1 (2020\-11\-23) .sp No significant changes. .SS v20.2.0 (2020\-11\-21) .SS Features \- 20.2.0 .INDENT 0.0 .IP \(bu 2 Optionally skip VCS ignore directive for entire virtualenv directory, using option \fBno\-vcs\-ignore\fP \%<#\:no-vcs-ignore>, by default \fBFalse\fP\&. (#2003 \%) .IP \(bu 2 Add \fB\-\-read\-only\-app\-data\fP option to allow for creation based on an existing app data cache which is non\-writable. This may be useful (for example) to produce a docker image where the app\-data is pre\-populated. .INDENT 2.0 .INDENT 3.5 .sp .EX ENV \e VIRTUALENV_OVERRIDE_APP_DATA=/opt/virtualenv/cache \e VIRTUALENV_SYMLINK_APP_DATA=1 RUN virtualenv venv && rm \-rf venv ENV VIRTUALENV_READ_ONLY_APP_DATA=1 USER nobody # this virtualenv has symlinks into the read\-only app\-data cache RUN virtualenv /tmp/venv .EE .UNINDENT .UNINDENT .sp Patch by @asottile \%\&. (#2009 \%) .UNINDENT .SS Bugfixes \- 20.2.0 .INDENT 0.0 .IP \(bu 2 Fix processing of the \fBVIRTUALENV_PYTHON\fP environment variable and make it multi\-value as well (separated by comma) \- by @pneff \%\&. (#1998 \%) .UNINDENT .SS v20.1.0 (2020\-10\-25) .SS Features \- 20.1.0 .INDENT 0.0 .IP \(bu 2 The python specification can now take one or more values, first found is used to create the virtual environment \- by @gaborbernat \%\&. (#1995 \%) .UNINDENT .SS v20.0.35 (2020\-10\-15) .SS Bugfixes \- 20.0.35 .INDENT 0.0 .IP \(bu 2 Bump embedded setuptools from \fB50.3.0\fP to \fB50.3.1\fP \- by @gaborbernat \%\&. (#1982 \%) .IP \(bu 2 After importing virtualenv passing cwd to a subprocess calls breaks with \fBinvalid directory\fP \- by @gaborbernat \%\&. (#1983 \%) .UNINDENT .SS v20.0.34 (2020\-10\-12) .SS Bugfixes \- 20.0.34 .INDENT 0.0 .IP \(bu 2 Align with venv module when creating virtual environments with builtin creator on Windows 3.7 and later \- by @gaborbernat \%\&. (#1782 \%) .IP \(bu 2 Handle Cygwin path conversion in the activation script \- by @davidcoghlan \%\&. (#1969 \%) .UNINDENT .SS v20.0.33 (2020\-10\-04) .SS Bugfixes \- 20.0.33 .INDENT 0.0 .IP \(bu 2 Fix \fBNone\fP type error in cygwin if POSIX path in dest \- by @danyeaw \%\&. (#1962 \%) .IP \(bu 2 Fix Python 3.4 incompatibilities (added back to the CI) \- by @gaborbernat \%\&. (#1963 \%) .UNINDENT .SS v20.0.32 (2020\-10\-01) .SS Bugfixes \- 20.0.32 .INDENT 0.0 .IP \(bu 2 For activation scripts always use UNIX line endings (unless it\(aqs BATCH shell related) \- by @saytosid \%\&. (#1818 \%) .IP \(bu 2 Upgrade embedded pip to \fB20.2.1\fP and setuptools to \fB49.4.0\fP \- by @gaborbernat \%\&. (#1918 \%) .IP \(bu 2 Avoid spawning new windows when doing seed package upgrades in the background on Windows \- by @gaborbernat \%\&. (#1928 \%) .IP \(bu 2 Fix a bug that reading and writing on the same file may cause race on multiple processes. (#1938 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB50.2.0\fP and pip to \fB20.2.3\fP \- by @gaborbernat \%\&. (#1939 \%) .IP \(bu 2 Provide correct path for bash activator in cygwin or msys2 \- by @danyeaw \%\&. (#1940 \%) .IP \(bu 2 Relax importlib requirement to allow version<3 \- by @usamasadiq \% (#1953 \%) .IP \(bu 2 pth files were not processed on CPython2 if $PYTHONPATH was pointing to site\-packages/ \- by @navytux \%\&. (#1959 \%) (#1960 \%) .UNINDENT .SS v20.0.31 (2020\-08\-17) .SS Bugfixes \- 20.0.31 .INDENT 0.0 .IP \(bu 2 Upgrade embedded pip to \fB20.2.1\fP, setuptools to \fB49.6.0\fP and wheel to \fB0.35.1\fP \- by @gaborbernat \%\&. (#1918 \%) .UNINDENT .SS v20.0.30 (2020\-08\-04) .SS Bugfixes \- 20.0.30 .INDENT 0.0 .IP \(bu 2 Upgrade pip to \fB20.2.1\fP and setuptools to \fB49.2.1\fP \- by @gaborbernat \%\&. (#1915 \%) .UNINDENT .SS v20.0.29 (2020\-07\-31) .SS Bugfixes \- 20.0.29 .INDENT 0.0 .IP \(bu 2 Upgrade embedded pip from version \fB20.1.2\fP to \fB20.2\fP \- by @gaborbernat \%\&. (#1909 \%) .UNINDENT .SS v20.0.28 (2020\-07\-24) .SS Bugfixes \- 20.0.28 .INDENT 0.0 .IP \(bu 2 Fix test suite failing if run from system Python \- by @gaborbernat \%\&. (#1882 \%) .IP \(bu 2 Provide \fBsetup_logging\fP flag to python API so that users can bypass logging handling if their application already performs this \- by @gaborbernat \%\&. (#1896 \%) .IP \(bu 2 Use \fB\en\fP instead if \fB\er\en\fP as line separator for report (because Python already performs this transformation automatically upon write to the logging pipe) \- by @gaborbernat \%\&. (#1905 \%) .UNINDENT .SS v20.0.27 (2020\-07\-15) .SS Bugfixes \- 20.0.27 .INDENT 0.0 .IP \(bu 2 No longer preimport threading to fix support for gpython \% and gevent \% \- by @navytux \%\&. (#1897 \%) .IP \(bu 2 Upgrade setuptools from \fB49.2.0\fP on \fBPython 3.5+\fP \- by @gaborbernat \%\&. (#1898 \%) .UNINDENT .SS v20.0.26 (2020\-07\-07) .SS Bugfixes \- 20.0.26 .INDENT 0.0 .IP \(bu 2 Bump dependency \fBdistutils >= 0.3.1\fP \- by @gaborbernat \%\&. (#1880 \%) .IP \(bu 2 Improve periodic update handling: .INDENT 2.0 .IP \(bu 2 better logging output while running and enable logging on background process call ( \fB_VIRTUALENV_PERIODIC_UPDATE_INLINE\fP may be used to debug behavior inline) .IP \(bu 2 fallback to unverified context when querying the PyPi for release date, .IP \(bu 2 stop downloading wheels once we reach the embedded version, .UNINDENT .sp by @gaborbernat \%\&. (#1883 \%) .IP \(bu 2 Do not print error message if the application exists with \fBSystemExit(0)\fP \- by @gaborbernat \%\&. (#1885 \%) .IP \(bu 2 Upgrade embedded setuptools from \fB47.3.1\fP to \fB49.1.0\fP for Python \fB3.5+\fP \- by @gaborbernat \%\&. (#1887 \%) .UNINDENT .SS v20.0.25 (2020\-06\-23) .SS Bugfixes \- 20.0.25 .INDENT 0.0 .IP \(bu 2 Fix that when the \fBapp\-data\fP seeders image creation fails the exception is silently ignored. Avoid two virtual environment creations to step on each others toes by using a lock while creating the base images. By @gaborbernat \%\&. (#1869 \%) .UNINDENT .SS v20.0.24 (2020\-06\-22) .SS Features \- 20.0.24 .INDENT 0.0 .IP \(bu 2 Ensure that the seeded packages do not get too much out of date: .INDENT 2.0 .IP \(bu 2 add a CLI flag that triggers upgrade of embedded wheels under \fBupgrade\-embed\-wheels\fP \%<#\:upgrade-embed-wheels> .IP \(bu 2 periodically (once every 14 days) upgrade the embedded wheels in a background process, and use them if they have been released for more than 28 days (can be disabled via \fBno\-periodic\-update\fP \%<#\:no-periodic-update>) .UNINDENT .sp More details under Wheel acquisition \%<#\:wheels> \- by @gaborbernat \%\&. (#1821 \%) .IP \(bu 2 Upgrade embed wheel content: .INDENT 2.0 .IP \(bu 2 ship wheels for Python \fB3.9\fP and \fB3.10\fP .IP \(bu 2 upgrade setuptools for Python \fB3.5+\fP from \fB47.1.1\fP to \fB47.3.1\fP .UNINDENT .sp by @gaborbernat \%\&. (#1841 \%) .IP \(bu 2 Display the installed seed package versions in the final summary output, for example: .INDENT 2.0 .INDENT 3.5 .sp .EX created virtual environment CPython3.8.3.final.0\-64 in 350ms creator CPython3Posix(dest=/x, clear=True, global=False) seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/y/virtualenv) added seed packages: pip==20.1.1, setuptools==47.3.1, wheel==0.34.2 .EE .UNINDENT .UNINDENT .sp by @gaborbernat \%\&. (#1864 \%) .UNINDENT .SS Bugfixes \- 20.0.24 .INDENT 0.0 .IP \(bu 2 Do not generate/overwrite \fB\&.gitignore\fP if it already exists at destination path \- by @gaborbernat \%\&. (#1862 \%) .IP \(bu 2 Improve error message for no \fB\&.dist\-info\fP inside the \fBapp\-data\fP copy seeder \- by @gaborbernat \%\&. (#1867 \%) .UNINDENT .SS Improved Documentation \- 20.0.24 .INDENT 0.0 .IP \(bu 2 How seeding mechanisms discover (and automatically keep it up to date) wheels at Wheel acquisition \%<#\:wheels> \- by @gaborbernat \%\&. (#1821 \%) .IP \(bu 2 How distributions should handle shipping their own embedded wheels at Distribution maintainer patching \%<#\:distribution-wheels> \- by @gaborbernat \%\&. (#1840 \%) .UNINDENT .SS v20.0.23 (2020\-06\-12) .SS Bugfixes \- 20.0.23 .INDENT 0.0 .IP \(bu 2 Fix typo in \fBsetup.cfg\fP \- by @RowdyHowell \%\&. (#1857 \%) .UNINDENT .SS v20.0.22 (2020\-06\-12) .SS Bugfixes \- 20.0.22 .INDENT 0.0 .IP \(bu 2 Relax \fBimportlib.resources\fP requirement to also allow version 2 \- by @asottile \%\&. (#1846 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB44.1.1\fP for python 2 and \fB47.1.1\fP for python3.5+ \- by @gaborbernat \%\&. (#1855 \%) .UNINDENT .SS v20.0.21 (2020\-05\-20) .SS Features \- 20.0.21 .INDENT 0.0 .IP \(bu 2 Generate ignore file for version control systems to avoid tracking virtual environments by default. Users should remove these files if still want to track. For now we support only \fBgit\fP by @gaborbernat \%\&. (#1806 \%) .UNINDENT .SS Bugfixes \- 20.0.21 .INDENT 0.0 .IP \(bu 2 Fix virtualenv fails sometimes when run concurrently, \fB\-\-clear\-app\-data\fP conflicts with \fBclear\fP \%<#\:clear> flag when abbreviation is turned on. To bypass this while allowing abbreviated flags on the command line we had to move it to \fBreset\-app\-data\fP \%<#\:reset-app-data> \- by @gaborbernat \%\&. (#1824 \%) .IP \(bu 2 Upgrade embedded \fBsetuptools\fP to \fB46.4.0\fP from \fB46.1.3\fP on Python \fB3.5+\fP, and \fBpip\fP from \fB20.1\fP to \fB20.1.1\fP \- by @gaborbernat \%\&. (#1827 \%) .IP \(bu 2 Seeder pip now correctly handles \fB\-\-extra\-search\-dir\fP \- by @frenzymadness \%\&. (#1834 \%) .UNINDENT .SS v20.0.20 (2020\-05\-04) .SS Bugfixes \- 20.0.20 .INDENT 0.0 .IP \(bu 2 Fix download fails with python 3.4 \- by @gaborbernat \%\&. (#1809 \%) .IP \(bu 2 Fixes older CPython2 versions use \fB_get_makefile_filename\fP instead of \fBget_makefile_filename\fP on \fBsysconfig\fP \- by @ianw \%\&. (#1810 \%) .IP \(bu 2 Fix download is \fBTrue\fP by default \- by @gaborbernat \%\&. (#1813 \%) .IP \(bu 2 Fail \fBapp\-data\fP seed operation when wheel download fails and better error message \- by @gaborbernat \%\&. (#1814 \%) .UNINDENT .SS v20.0.19 (2020\-05\-03) .SS Bugfixes \- 20.0.19 .INDENT 0.0 .IP \(bu 2 Fix generating a Python 2 environment from Python 3 creates invalid python activator \- by @gaborbernat \%\&. (#1776 \%) .IP \(bu 2 Fix pinning seed packages via \fBapp\-data\fP seeder raised \fBInvalid Requirement\fP \- by @gaborbernat \%\&. (#1779 \%) .IP \(bu 2 Do not stop interpreter discovery if we fail to find the system interpreter for a executable during discovery \- by @gaborbernat \%\&. (#1781 \%) .IP \(bu 2 On CPython2 POSIX platforms ensure \fBsyconfig.get_makefile_filename\fP exists within the virtual environment (this is used by some c\-extension based libraries \- e.g. numpy \- for building) \- by @gaborbernat \%\&. (#1783 \%) .IP \(bu 2 Better handling of options \fBcopies\fP \%<#\:copies> and \fBsymlinks\fP \%<#\:symlinks>\&. Introduce priority of where the option is set to follow the order: CLI, env var, file, hardcoded. If both set at same level prefers copy over symlink. \- by @gaborbernat \%\&. (#1784 \%) .IP \(bu 2 Upgrade pip for Python \fB2.7\fP and \fB3.5+\fP from \fB20.0.2\fP to \fB20.1\fP \- by @gaborbernat \%\&. (#1793 \%) .IP \(bu 2 Fix CPython is not discovered from Windows registry, and discover pythons from Windows registry in decreasing order by version \- by @gaborbernat \%\&. (#1796 \%) .IP \(bu 2 Fix symlink detection for creators \- by @asottile \% (#1803 \%) .UNINDENT .SS v20.0.18 (2020\-04\-16) .SS Bugfixes \- 20.0.18 .INDENT 0.0 .IP \(bu 2 Importing setuptools before cli_run could cause our python information query to fail due to setuptools patching \fBdistutils.dist.Distribution\fP \- by @gaborbernat \%\&. (#1771 \%) .UNINDENT .SS v20.0.17 (2020\-04\-09) .SS Features \- 20.0.17 .INDENT 0.0 .IP \(bu 2 Extend environment variables checked for configuration to also check aliases (e.g. setting either \fBVIRTUALENV_COPIES\fP or \fBVIRTUALENV_ALWAYS_COPY\fP will work) \- by @gaborbernat \%\&. (#1763 \%) .UNINDENT .SS v20.0.16 (2020\-04\-04) .SS Bugfixes \- 20.0.16 .INDENT 0.0 .IP \(bu 2 Allow seed wheel files inside the \fBextra\-search\-dir\fP \%<#\:extra-search-dir> folders that do not have \fBRequires\-Python\fP metadata specified, these are considered compatible with all python versions \- by @gaborbernat \%\&. (#1757 \%) .UNINDENT .SS v20.0.15 (2020\-03\-27) .SS Features \- 20.0.15 .INDENT 0.0 .IP \(bu 2 Upgrade embedded setuptools to \fB46.1.3\fP from \fB46.1.1\fP \- by @gaborbernat \%\&. (#1752 \%) .UNINDENT .SS v20.0.14 (2020\-03\-25) .SS Features \- 20.0.14 .INDENT 0.0 .IP \(bu 2 Remove \fB__PYVENV_LAUNCHER__\fP on macOs for Python \fB3.7.(<8)\fP and \fB3.8.(<3)\fP on interpreter startup via \fBpth\fP file, this pulls in the upstream patch \% \- by @gaborbernat \%\&. (#1704 \%) .IP \(bu 2 Upgrade embedded setuptools for Python \fB3.5+\fP to \fB46.1.1\fP, for Python \fB2.7\fP to \fB44.1.0\fP \- by @gaborbernat \%\&. (#1745 \%) .UNINDENT .SS Bugfixes \- 20.0.14 .INDENT 0.0 .IP \(bu 2 Fix discovery of interpreter by name from \fBPATH\fP that does not match a spec format \- by @gaborbernat \%\&. (#1746 \%) .UNINDENT .SS v20.0.13 (2020\-03\-19) .SS Bugfixes \- 20.0.13 .INDENT 0.0 .IP \(bu 2 Do not fail when the pyc files is missing for the host Python 2 \- by @gaborbernat \%\&. (#1738 \%) .IP \(bu 2 Support broken Packaging pythons that put the include headers under distutils pattern rather than sysconfig one \- by @gaborbernat \%\&. (#1739 \%) .UNINDENT .SS v20.0.12 (2020\-03\-19) .SS Bugfixes \- 20.0.12 .INDENT 0.0 .IP \(bu 2 Fix relative path discovery of interpreters \- by @gaborbernat \%\&. (#1734 \%) .UNINDENT .SS v20.0.11 (2020\-03\-18) .SS Features \- 20.0.11 .INDENT 0.0 .IP \(bu 2 Improve error message when the host python does not satisfy invariants needed to create virtual environments (now we print which host files are incompatible/missing and for which creators when no supported creator can be matched, however we found creators that can describe the given Python interpreter \- will still print no supported creator for Jython, however print exactly what host files do not allow creation of virtual environments in case of CPython/PyPy) \- by @gaborbernat \%\&. (#1716 \%) .UNINDENT .SS Bugfixes \- 20.0.11 .INDENT 0.0 .IP \(bu 2 Support Python 3 Framework distributed via XCode in macOs Catalina and before \- by @gaborbernat \%\&. (#1663 \%) .IP \(bu 2 Fix Windows Store Python support, do not allow creation via symlink as that\(aqs not going to work by design \- by @gaborbernat \%\&. (#1709 \%) .IP \(bu 2 Fix \fBactivate_this.py\fP throws \fBAttributeError\fP on Windows when virtual environment was created via cross python mechanism \- by @gaborbernat \%\&. (#1710 \%) .IP \(bu 2 Fix \fB\-\-no\-pip\fP, \fB\-\-no\-setuptools\fP, \fB\-\-no\-wheel\fP not being respected \- by @gaborbernat \%\&. (#1712 \%) .IP \(bu 2 Allow missing \fB\&.py\fP files if a compiled \fB\&.pyc\fP version is available \- by @tucked \%\&. (#1714 \%) .IP \(bu 2 Do not fail if the distutils/setuptools patch happens on a C\-extension loader (such as \fBzipimporter\fP on Python 3.7 or earlier) \- by @gaborbernat \%\&. (#1715 \%) .IP \(bu 2 Support Python 2 implementations that require the landmark files and \fBsite.py\fP to be in platform standard library instead of the standard library path of the virtual environment (notably some RHEL ones, such as the Docker image \fBamazonlinux:1\fP) \- by @gaborbernat \%\&. (#1719 \%) .IP \(bu 2 Allow the test suite to pass even when called with the system Python \- to help repackaging of the tool for Linux distributions \- by @gaborbernat \%\&. (#1721 \%) .IP \(bu 2 Also generate \fBpipx.y\fP console script beside \fBpip\-x.y\fP to be compatible with how pip installs itself \- by @gaborbernat \%\&. (#1723 \%) .IP \(bu 2 Automatically create the application data folder if it does not exists \- by @gaborbernat \%\&. (#1728 \%) .UNINDENT .SS Improved Documentation \- 20.0.11 .INDENT 0.0 .IP \(bu 2 supports \%<#\:compatibility-requirements> details now explicitly what Python installations we support \- by @gaborbernat \%\&. (#1714 \%) .UNINDENT .SS v20.0.10 (2020\-03\-10) .SS Bugfixes \- 20.0.10 .INDENT 0.0 .IP \(bu 2 Fix acquiring python information might be altered by distutils configuration files generating incorrect layout virtual environments \- by @gaborbernat \%\&. (#1663 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB46.0.0\fP from \fB45.3.0\fP on Python \fB3.5+\fP \- by @gaborbernat \%\&. (#1702 \%) .UNINDENT .SS Improved Documentation \- 20.0.10 .INDENT 0.0 .IP \(bu 2 Document requirements (pip + index server) when installing via pip under the installation section \- by @gaborbernat \%\&. (#1618 \%) .IP \(bu 2 Document installing from non PEP\-518 systems \- @gaborbernat \%\&. (#1619 \%) .IP \(bu 2 Document installing latest unreleased version from Github \- @gaborbernat \%\&. (#1620 \%) .UNINDENT .SS v20.0.9 (2020\-03\-08) .SS Bugfixes \- 20.0.9 .INDENT 0.0 .IP \(bu 2 \fBpythonw.exe\fP works as \fBpython.exe\fP on Windows \- by @gaborbernat \%\&. (#1686 \%) .IP \(bu 2 Handle legacy loaders for virtualenv import hooks used to patch distutils configuration load \- by @gaborbernat \%\&. (#1690 \%) .IP \(bu 2 Support for python 2 platforms that store landmark files in \fBplatstdlib\fP over \fBstdlib\fP (e.g. RHEL) \- by @gaborbernat \%\&. (#1694 \%) .IP \(bu 2 Upgrade embedded setuptools to \fB45.3.0\fP from \fB45.2.0\fP for Python \fB3.5+\fP \- by @gaborbernat \%\&. (#1699 \%) .UNINDENT .SS v20.0.8 (2020\-03\-04) .SS Bugfixes \- 20.0.8 .INDENT 0.0 .IP \(bu 2 Having distutils configuration \% files that set \fBprefix\fP and \fBinstall_scripts\fP cause installation of packages in the wrong location \- by @gaborbernat \%\&. (#1663 \%) .IP \(bu 2 Fix \fBPYTHONPATH\fP being overridden on Python 2 — by @jd \%\&. (#1673 \%) .IP \(bu 2 Fix list configuration value parsing from config file or environment variable \- by @gaborbernat \%\&. (#1674 \%) .IP \(bu 2 Fix Batch activation script shell prompt to display environment name by default \- by @spetafree \%\&. (#1679 \%) .IP \(bu 2 Fix startup on Python 2 is slower for virtualenv \- this was due to setuptools calculating it\(aqs working set distribution \- by @gaborbernat \%\&. (#1682 \%) .IP \(bu 2 Fix entry points are not populated for editable installs on Python 2 due to setuptools working set being calculated before \fBeasy_install.pth\fP runs \- by @gaborbernat \%\&. (#1684 \%) .IP \(bu 2 Fix \fBattr:\fP import fails for setuptools \- by @gaborbernat \%\&. (#1685 \%) .UNINDENT .SS v20.0.7 (2020\-02\-26) .SS Bugfixes \- 20.0.7 .INDENT 0.0 .IP \(bu 2 Disable distutils fixup for python 3 until pypa/pip #7778 \% is fixed and released \- by @gaborbernat \%\&. (#1669 \%) .UNINDENT .SS v20.0.6 (2020\-02\-26) .SS Bugfixes \- 20.0.6 .INDENT 0.0 .IP \(bu 2 Fix global site package always being added with bundled macOs python framework builds \- by @gaborbernat \%\&. (#1561 \%) .IP \(bu 2 Fix generated scripts use host version info rather than target \- by @gaborbernat \%\&. (#1600 \%) .IP \(bu 2 Fix circular prefix reference with single elements (accept these as if they were system executables, print a info about them referencing themselves) \- by @gaborbernat \%\&. (#1632 \%) .IP \(bu 2 Handle the case when the application data folder is read\-only: .INDENT 2.0 .IP \(bu 2 the application data folder is now controllable via \fBapp\-data\fP \%<#\:app-data>, .IP \(bu 2 \fBclear\-app\-data\fP now cleans the entire application data folder, not just the \fBapp\-data\fP seeder path, .IP \(bu 2 check if the application data path passed in does not exist or is read\-only, and fallback to a temporary directory, .IP \(bu 2 temporary directory application data is automatically cleaned up at the end of execution, .IP \(bu 2 \fBsymlink\-app\-data\fP \%<#\:symlink-app-data> is always \fBFalse\fP when the application data is temporary .UNINDENT .sp by @gaborbernat \%\&. (#1640 \%) .IP \(bu 2 Fix PyPy 2 builtin modules are imported from standard library, rather than from builtin \- by @gaborbernat \%\&. (#1652 \%) .IP \(bu 2 Fix creation of entry points when path contains spaces \- by @nsoranzo \%\&. (#1660 \%) .IP \(bu 2 Fix relative paths for the zipapp (for python \fB3.7+\fP) \- by @gaborbernat \%\&. (#1666 \%) .UNINDENT .SS v20.0.5 (2020\-02\-21) .SS Features \- 20.0.5 .INDENT 0.0 .IP \(bu 2 Also create \fBpythonX.X\fP executables when creating pypy virtualenvs \- by @asottile \% (#1612 \%) .IP \(bu 2 Fail with better error message if trying to install source with unsupported \fBsetuptools\fP, allow \fBsetuptools\-scm >= 2\fP and move to legacy \fBsetuptools\-scm\fP format to support better older platforms (\fBCentOS 7\fP and such) \- by @gaborbernat \%\&. (#1621 \%) .IP \(bu 2 Report of the created virtual environment is now split across four short lines rather than one long \- by @gaborbernat \% (#1641 \%) .UNINDENT .SS Bugfixes \- 20.0.5 .INDENT 0.0 .IP \(bu 2 Add macOs Python 2 Framework support (now we test it with the CI via brew) \- by @gaborbernat \% (#1561 \%) .IP \(bu 2 Fix losing of libpypy\-c.so when the pypy executable is a symlink \- by @asottile \% (#1614 \%) .IP \(bu 2 Discover python interpreter in a case insensitive manner \- by @PrajwalM2212 \% (#1624 \%) .IP \(bu 2 Fix cross interpreter support when the host python sets \fBsys.base_executable\fP based on \fB__PYVENV_LAUNCHER__\fP \- by @cjolowicz \% (#1643 \%) .UNINDENT .SS v20.0.4 (2020\-02\-14) .SS Features \- 20.0.4 .INDENT 0.0 .IP \(bu 2 When aliasing interpreters, use relative symlinks \- by @asottile \%\&. (#1596 \%) .UNINDENT .SS Bugfixes \- 20.0.4 .INDENT 0.0 .IP \(bu 2 Allow the use of \fB/\fP as pathname component separator on Windows \- by \fBvphilippon\fP (#1582 \%) .IP \(bu 2 Lower minimal version of six required to 1.9 \- by \fBssbarnea\fP (#1606 \%) .UNINDENT .SS v20.0.3 (2020\-02\-12) .SS Bugfixes \- 20.0.3 .INDENT 0.0 .IP \(bu 2 On Python 2 with Apple Framework builds the global site package is no longer added when the \fBsystem\-site\-packages\fP \%<#\:system-site-packages> is not specified \- by @gaborbernat \%\&. (#1561 \%) .IP \(bu 2 Fix system python discovery mechanism when prefixes contain relative parts (e.g. \fB\&..\fP) by resolving paths within the python information query \- by @gaborbernat \%\&. (#1583 \%) .IP \(bu 2 Expose a programmatic API as \fBfrom virtualenv import cli_run\fP \- by @gaborbernat \%\&. (#1585 \%) .IP \(bu 2 Fix \fBapp\-data\fP \fBseeder\fP \%<#\:seeder> injects a extra \fB\&.dist\-info.virtualenv\fP path that breaks \fBimportlib.metadata\fP, now we inject an extra \fB\&.virtualenv\fP \- by @gaborbernat \%\&. (#1589 \%) .UNINDENT .SS Improved Documentation \- 20.0.3 .INDENT 0.0 .IP \(bu 2 Document a programmatic API as \fBfrom virtualenv import cli_run\fP under Python \%<#\:programmatic-api> \- by @gaborbernat \%\&. (#1585 \%) .UNINDENT .SS v20.0.2 (2020\-02\-11) .SS Features \- 20.0.2 .INDENT 0.0 .IP \(bu 2 Print out a one line message about the created virtual environment when no \fBverbose\fP \%<#\:verbose> is set, this can now be silenced to get back the original behavior via the \fBquiet\fP \%<#\:quiet> flag \- by @pradyunsg \%\&. (#1557 \%) .IP \(bu 2 Allow virtualenv\(aqs app data cache to be overridden by \fBVIRTUALENV_OVERRIDE_APP_DATA\fP \- by @asottile \%\&. (#1559 \%) .IP \(bu 2 Passing in the virtual environment name/path is now required (no longer defaults to \fBvenv\fP) \- by @gaborbernat \%\&. (#1568 \%) .IP \(bu 2 Add a CLI flag \fBwith\-traceback\fP \%<#\:with-traceback> that allows displaying the stacktrace of the virtualenv when a failure occurs \- by @gaborbernat \%\&. (#1572 \%) .UNINDENT .SS Bugfixes \- 20.0.2 .INDENT 0.0 .IP \(bu 2 Support long path names for generated virtual environment console entry points (such as \fBpip\fP) when using the \fBapp\-data\fP \fBseeder\fP \%<#\:seeder> \- by @gaborbernat \%\&. (#997 \%) .IP \(bu 2 Improve python discovery mechanism: .INDENT 2.0 .IP \(bu 2 do not fail if there are executables that fail to query (e.g. for not having execute access to it) on the \fBPATH\fP, .IP \(bu 2 beside the prefix folder also try with the platform dependent binary folder within that, .UNINDENT .sp by @gaborbernat \%\&. (#1545 \%) .IP \(bu 2 When copying (either files or trees) do not copy the permission bits, last access time, last modification time, and flags as access to these might be forbidden (for example in case of the macOs Framework Python) and these are not needed for the user to use the virtual environment \- by @gaborbernat \%\&. (#1561 \%) .IP \(bu 2 While discovering a python executables interpreters that cannot be queried are now displayed with info level rather than warning, so now they\(aqre no longer shown by default (these can be just executables to which we don\(aqt have access or that are broken, don\(aqt warn if it\(aqs not the target Python we want) \- by @gaborbernat \%\&. (#1574 \%) .IP \(bu 2 The \fBapp\-data\fP \fBseeder\fP \%<#\:seeder> no longer symlinks the packages on UNIX and copies on Windows. Instead by default always copies, however now has the \fBsymlink\-app\-data\fP \%<#\:symlink-app-data> flag allowing users to request this less robust but faster method \- by @gaborbernat \%\&. (#1575 \%) .UNINDENT .SS Improved Documentation \- 20.0.2 .INDENT 0.0 .IP \(bu 2 Add link to the legacy documentation \% for the changelog by @jezdez \%\&. (#1547 \%) .IP \(bu 2 Fine tune the documentation layout: default width of theme, allow tables to wrap around, soft corners for code snippets \- by @pradyunsg \%\&. (#1548 \%) .UNINDENT .SS v20.0.1 (2020\-02\-10) .SS Features \- 20.0.1 .INDENT 0.0 .IP \(bu 2 upgrade embedded setuptools to \fB45.2.0\fP from \fB45.1.0\fP for Python \fB3.4+\fP \- by @gaborbernat \%\&. (#1554 \%) .UNINDENT .SS Bugfixes \- 20.0.1 .INDENT 0.0 .IP \(bu 2 Virtual environments created via relative path on Windows creates bad console executables \- by @gaborbernat \%\&. (#1552 \%) .IP \(bu 2 Seems sometimes venvs created set their base executable to themselves; we accept these without question, so we handle virtual environments as system pythons causing issues \- by @gaborbernat \%\&. (#1553 \%) .UNINDENT .SS v20.0.0. (2020\-02\-10) .SS Improved Documentation \- 20.0.0. .INDENT 0.0 .IP \(bu 2 Fixes typos, repeated words and inconsistent heading spacing. Rephrase parts of the development documentation and CLI documentation. Expands shorthands like \fBenv var\fP and \fBconfig\fP to their full forms. Uses descriptions from respective documentation, for projects listed in \fBrelated links\fP \- by @pradyunsg \%\&. (#1540 \%) .UNINDENT .SS v20.0.0b2 (2020\-02\-04) .SS Features \- 20.0.0b2 .INDENT 0.0 .IP \(bu 2 Improve base executable discovery mechanism: .INDENT 2.0 .IP \(bu 2 print at debug level why we refuse some candidates, .IP \(bu 2 when no candidates match exactly, instead of hard failing fallback to the closest match where the priority of matching attributes is: python implementation, major version, minor version, architecture, patch version, release level and serial (this is to facilitate things to still work when the OS upgrade replace/upgrades the system python with a never version, than what the virtualenv host python was created with), .IP \(bu 2 always resolve system_executable information during the interpreter discovery, and the discovered environment is the system interpreter instead of the venv/virtualenv (this happened before lazily the first time we accessed, and caused reporting that the created virtual environment is of type of the virtualenv host python version, instead of the system pythons version \- these two can differ if the OS upgraded the system python underneath and the virtualenv host was created via copy), .UNINDENT .sp by @gaborbernat \%\&. (#1515 \%) .IP \(bu 2 Generate \fBbash\fP and \fBfish\fP activators on Windows too (as these can be available with git bash, cygwin or mysys2) \- by @gaborbernat \%\&. (#1527 \%) .IP \(bu 2 Upgrade the bundled \fBwheel\fP package from \fB0.34.0\fP to \fB0.34.2\fP \- by @gaborbernat \%\&. (#1531 \%) .UNINDENT .SS Bugfixes \- 20.0.0b2 .INDENT 0.0 .IP \(bu 2 Bash activation script should have no extensions instead of \fB\&.sh\fP (this fixes the virtualenvwrapper \% integration) \- by @gaborbernat \%\&. (#1508 \%) .IP \(bu 2 Show less information when we run with a single verbosity (\fB\-v\fP): .INDENT 2.0 .IP \(bu 2 no longer shows accepted interpreters information (as the last proposed one is always the accepted one), .IP \(bu 2 do not display the \fBstr_spec\fP attribute for \fBPythonSpec\fP as these can be deduced from the other attributes, .IP \(bu 2 for the \fBapp\-data\fP seeder do not show the type of lock, only the path to the app data directory, .UNINDENT .sp By @gaborbernat \%\&. (#1510 \%) .IP \(bu 2 Fixed cannot discover a python interpreter that has already been discovered under a different path (such is the case when we have multiple symlinks to the same interpreter) \- by @gaborbernat \%\&. (#1512 \%) .IP \(bu 2 Support relative paths for \fB\-p\fP \- by @gaborbernat \%\&. (#1514 \%) .IP \(bu 2 Creating virtual environments in parallel fail with cannot acquire lock within app data \- by @gaborbernat \%\&. (#1516 \%) .IP \(bu 2 pth files were not processed under Debian CPython2 interpreters \- by @gaborbernat \%\&. (#1517 \%) .IP \(bu 2 Fix prompt not displayed correctly with upcoming fish 3.10 due to us not preserving \fB$pipestatus\fP \- by @krobelus \%\&. (#1530 \%) .IP \(bu 2 Stable order within \fBpyenv.cfg\fP and add \fBinclude\-system\-site\-packages\fP only for creators that reference a global Python \- by user:\fIgaborbernat\fP\&. (#1535 \%) .UNINDENT .SS Improved Documentation \- 20.0.0b2 .INDENT 0.0 .IP \(bu 2 Create the first iteration of the new documentation \- by @gaborbernat \%\&. (#1465 \%) .IP \(bu 2 Project readme is now of type MarkDown instead of reStructuredText \- by @gaborbernat \%\&. (#1531 \%) .UNINDENT .SS v20.0.0b1 (2020\-01\-28) .INDENT 0.0 .IP \(bu 2 First public release of the rewrite. Everything is brand new and just added. .IP \(bu 2 \fB\-\-download\fP defaults to \fBFalse\fP .IP \(bu 2 No longer replaces builtin \fBsite\fP module with custom version baked within virtualenv code itself \%\&. A simple shim module is used to fix up things on Python 2 only. .UNINDENT .sp \fBWarning:\fP .INDENT 0.0 .INDENT 3.5 The current virtualenv is the second iteration of implementation. From version \fB0.8\fP all the way to \fB16.7.9\fP we numbered the first iteration. Version \fB20.0.0b1\fP is a complete rewrite of the package, and as such this release history starts from there. The old changelog is still available in the legacy branch documentation \%\&. .UNINDENT .UNINDENT .SH Author Author name not set .SH Copyright 2007-2026, PyPA, PyPA .\" End of generated man page.