Open-source project
pyenv/pyenv-virtualenv avatar
pyenv/pyenv-virtualenv

pyenv-virtualenv keeps every environment inside pyenv's versions directory

a pyenv plugin to manage virtualenv (a.k.a. python-virtualenv)

6,726 stars429 forksShellMIT

At a glance

What is it?
pyenv-virtualenv is a pyenv plugin that creates and switches virtualenv and conda environments inside pyenv's own versions directory, so a name in .python-version selects one. Shell wiring, package management and Windows sit outside its scope.
Who is it for?
pyenv-virtualenv earns its place on a machine that already runs pyenv and wants an environment selectable by name from a `.python-version` file, because that is the one thing the built-in `venv` module will not do for you. It is the wrong choice if you already manage environments with virtualenvwrapper, and the notes themselves say `pyenv-virtualenvwrapper` may help those users (additionally) manage their virtualenvs.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • pyenv/pyenv-virtualenv6,726 ★ · -1
Star history
Repository2026-09-232026-11-14
pyenv/pyenv-virtualenv6.7k6.7k

-1 Stars in the last 52 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

Environments live under $(pyenv root)/versions, so the listing shows each one twice

pyenv-virtualenv does not keep environments in a directory of its own. Running `pyenv virtualenv 2.7.10 my-virtual-env-2.7.10` creates the environment under `$(pyenv root)/versions`, and `pyenv virtualenvs` prints the list of existing virtualenvs and conda environments from there.

The output has two rows per environment. In the example the project ships, `2.7.10/envs/my-virtual-env-2.7.10` sits next to `my-virtual-env-2.7.10 (created from /home/yyuu/.pyenv/versions/2.7.10)`, and the note under the listing says the shorter one is just a symlink. That duplication is not an accident: the short alias is the form a `.python-version` file can carry.

sh
pyenv shell venv34
pyenv virtualenvs

The `*` marker shows which environment the shell is currently on. Any script that parses this output has to expect two rows per environment and decide which one it wants, which is a small trap for tooling that assumed one directory equals one environment.

The creating backend depends on which tools are already installed

`pyenv virtualenv` forwards its options to the underlying command that actually creates the environment, and the backends named are `conda`, `virtualenv` and `python -m venv`. Which one runs is not decided by the plugin. The single selection rule stated is that `python -m venv` is used when it is available and the `virtualenv` command is not. On a machine holding both `virtualenv` and CPython 3.3 or newer, `virtualenv` wins and `venv` never runs.

The option list lives behind `pyenv virtualenv --help` rather than in the notes, which matters the moment you pass flags. Because options are forwarded to whichever backend runs, a flag valid for one backend may not be valid for another.

A git clone installs the development branch until you pin a tag

The plugin install is a clone into the plugins directory of whatever pyenv root you have:

bash
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv

Two limits come with it. This installs the latest development version rather than a tagged release; pinning one means checking out its tag inside that directory, and following along afterwards means running `git pull` there. The command uses `pyenv root` to discover the active installation, including a non-default root. Confirm it points to the intended installation before cloning.

macOS has a second route, and it is the one recommended when pyenv itself came from Homebrew:

bash
brew install pyenv-virtualenv

`brew install --HEAD pyenv-virtualenv` tracks the development version instead. Either route leaves the shell setup to you.

Auto-activation reads .python-version and needs one line in your rc file

Nothing switches environments automatically until one line reaches a shell startup file. For bash:

bash
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc

zsh users put it in `~/.zshrc` instead. Fish needs its own file, `~/.config/fish/config.fish`, and its own syntax:

fish
status --is-interactive; and pyenv virtualenv-init - | source

Then the shell is restarted, which the install steps do with:

bash
exec "$SHELL"

From that point the plugin watches for `.python-version` files. Entering a directory whose `.python-version` names a valid environment activates it, and leaving deactivates it. The name has to match something `pyenv virtualenvs` prints, either the short alias or the full nested path. A `.python-version` holding a plain interpreter version still steers pyenv, but there is no environment for the plugin to switch into.

WSL line endings break the plugin before anything else does

The single failure the install notes describe belongs to Windows Subsystem for Linux users whose checkout carries CRLF line endings. This is a Shell project whose entry points sit in `shims/`, `bin/` and `libexec/`, so a script arriving with carriage returns fails at execution rather than at load. The documented fix is a global Git setting:

bash
git config --global core.autocrlf input

`--global` is the part worth pausing on. The setting applies to every repository on the machine, not just this plugin, and it changes what Git writes on commit rather than what one checkout happens to contain. A team standardising on that is fine. A single developer who only wants this plugin fixed is editing a wider setting than the problem needs.

The repository root carries `install.sh`, a `test/` directory, a `.travis.yml` file, `CHANGELOG.md` and `MAINTENANCE.md`.

Deleting an environment: three commands with three different reaches

Removal has three routes. The core pyenv command takes the name:

sh
pyenv uninstall my-virtual-env

The plugin's own command targets the same environment:

sh
pyenv virtualenv-delete my-virtual-env

The third is manual. The notes state that removing the directories in `$(pyenv root)/versions` and `$(pyenv root)/versions/{version}/envs` deletes the virtualenv.

What the notes leave open is the leftover pointer. They say nothing about a `.python-version` file still naming a deleted environment. That file is created by `pyenv local` and cleared by it, so deleting the directory does not clear the selection that referred to it, and anyone automating removal has to handle the stale name separately.

conda environments get a nested path and rewrite PATH on activate

Conda environments are created the ordinary Anaconda or Miniconda way with `conda create`, but they appear in the plugin's listing with a nested path and are switched with the plugin's own commands:

sh
conda env list
pyenv activate miniconda3-3.9.1/envs/myenv

The name has to include the `envs` segment, which is exactly what `pyenv virtualenvs` prints for that environment. Activating one reports two PATH changes: the base installation's `bin` directory is discarded and the environment's own `bin` is prepended.

The same pair, `pyenv activate` and `pyenv deactivate`, covers virtualenvs and conda environments, so there is only one switch to learn. What the plugin does not do is take conda over. The authoritative environment list remains the one `conda env list` prints, and the two listings sit at different depths under `$(pyenv root)/versions`, which matters when a path from one gets pasted into the other.

Packages, platform scope and virtualenvwrapper stay outside the plugin

Four things sit outside this repository.

Packages are not managed here. Creating an environment and deciding what goes into it are separate steps.

Platform scope is the second limit. The project's own one-line description covers UNIX-like systems, and the two documented installs are a Git clone into a pyenv plugins directory and a Homebrew formula for macOS. No Windows install instructions appear in the installation section.

The third is the shell wiring described earlier, optional by design and manual by necessity.

The fourth is virtualenvwrapper. The notes say that users who already rely on it, and like it, may prefer `pyenv-virtualenvwrapper`, a separate plugin that adds to that management rather than replacing it.

CPython's `venv` module is the closest alternative and needs no plugin at all, since it ships with CPython 3.3 and newer. The real difference is where state lives. A `venv` environment is an ordinary directory you point at yourself, while an environment made through this plugin is registered under `$(pyenv root)/versions` and can therefore be named in a `.python-version` file.

Editorial conclusion

pyenv-virtualenv earns its place on a machine that already runs pyenv and wants an environment selectable by name from a `.python-version` file, because that is the one thing the built-in `venv` module will not do for you. It is the wrong choice if you already manage environments with virtualenvwrapper, and the notes themselves say `pyenv-virtualenvwrapper` may help those users (additionally) manage their virtualenvs. Verify three things before committing: which of `virtualenv`, `python -m venv` and `conda` is on the machine, since that decides both the backend and which flags are valid; that `pyenv root` resolves to your intended installation before cloning into its plugins directory; and that your shell startup file holds the `virtualenv-init` line, because without it no directory change will ever switch anything.

Frequently asked questions

What is pyenv virtualenv?

It is a pyenv plugin, written in Shell, that manages virtualenvs and conda environments for Python on UNIX-like systems. Environments are created under $(pyenv root)/versions, and the plugin adds the subcommands pyenv virtualenv, pyenv virtualenvs, pyenv activate, pyenv deactivate, pyenv virtualenv-delete and pyenv virtualenv-init.

How to install pyenv virtualenv?

Two documented routes: clone the repository into $(pyenv root)/plugins/pyenv-virtualenv, or on macOS run brew install pyenv-virtualenv, which is the recommended one when pyenv itself came from Homebrew. The clone tracks the development version, so check out a release tag if you need a fixed one, then add the virtualenv-init line to a shell startup file and restart the shell.

How to use pyenv virtualenv to create an environment?

Give it the interpreter version and a name, for example pyenv virtualenv 2.7.10 my-virtual-env-2.7.10. Pass only a name and it builds from whatever version pyenv is on at that moment. List what exists with pyenv virtualenvs, then switch to one with pyenv activate.

How to activate a pyenv virtualenv?

Two ways. Run pyenv activate with the environment name, or add eval "$(pyenv virtualenv-init -)" to a shell startup file so the plugin activates and deactivates environments as you enter and leave directories whose .python-version file names one.

Where is pyenv virtualenv located on disk?

Under $(pyenv root)/versions. The listing in the project's example shows each virtualenv twice, once nested as 2.7.10/envs/my-virtual-env-2.7.10 and once as the short name my-virtual-env-2.7.10, and the shorter entry is a symlink. conda environments sit in an envs subdirectory of a version directory, such as miniconda3-3.9.1/envs/myenv.

Can pyenv virtualenv be installed on Windows?

The stated scope is UNIX-like systems, and the two documented installs are a Git clone into the pyenv plugins directory and a Homebrew formula for macOS. The notes give no Windows instructions, and the one Windows-adjacent problem they address is CRLF line endings under WSL.

Official sources

  1. Issues
  2. License: MIT
  3. pyenv/pyenv-virtualenv on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pyenv-pyenv-virtualenv.svg)](https://hysenlabs.com/projects/pyenv-pyenv-virtualenv)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/pyenv-pyenv-virtualenv.svg)](https://hysenlabs.com/projects/pyenv-pyenv-virtualenv)

Was this useful?