Open-source project
pyenv/pyenv avatar
pyenv/pyenv

pyenv: a fork of rbenv that puts shims on PATH, skips virtualenvs, and does not do Windows

GitHub describes it as Simple Python version management. The repository metadata lists Shell as its primary language. The metadata lists the MIT license. This article stays within the project description and details documented in the GitHub repository README.

45,120 stars3,270 forksShellMIT

At a glance

What is it?
pyenv selects Python interpreters and nothing else. Its own contrast section draws three lines against pythonbrew and pythonz, and those three lines explain most of what surprises people: no Python dependency, no shell function to source, and no virtualenv management.
Who is it for?
pyenv fits a Unix developer who needs several Python versions on one machine and is comfortable with PATH, shims and a Bash-level tool. It is a poor fit if you want one command to make an isolated environment, because that is the job it declines, and a poor fit on native Windows, which it declines for a documented reason.
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 September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The contrast section declines three things, and virtualenv is one of them

The README has a section titled In contrast with pythonbrew and pythonz, pyenv does not, and its three entries are the clearest description of the tool. It does not depend on Python itself, because it was made from pure shell scripts and there is no bootstrap problem of Python. It does not need to be loaded into your shell, because the shim approach works by adding a directory to your PATH. And it does not manage virtualenv, where the advice is to create virtualenv yourself or to use the pyenv-virtualenv plugin to automate the process.

That last line is the answer to the most common question about the tool, and the project states it rather than leaving it implied. pyenv picks which interpreter runs. virtualenv, or the plugin that wraps it, isolates the packages inside one. Neither replaces the other, and installing pyenv does not give you environments.

The consequence is a workflow with two steps instead of one. A project needs a pyenv-selected Python, and then a virtualenv on top of it, which means a second tool in your path and a second thing to configure. The other two negatives are quieter wins: because there is no shell function to source, a new terminal works immediately, and because nothing is compiled against Python, a broken system Python cannot stop you installing another one.

The shim directory shadows python, and the README has a section on removing it

The mechanism is a directory of small executables placed early on PATH. When you type `python`, you get a shim, and the shim decides which real interpreter to run based on pyenv's version selection. The How It Works part of the README walks through this in three parts: understanding PATH, understanding shims, and understanding Python version selection, followed by locating the Python installations pyenv provides.

The Advanced Configuration part has a section titled Using Pyenv without shims, and another titled Running nested shells from Python-based programs. Both exist because the shim approach has a known edge, and naming them is a small admission that the mechanism is not invisible.

The consequence is that anything which does not go through PATH lookup bypasses pyenv entirely. A script with a hardcoded interpreter path, an absolute path baked into a shebang at install time, or a tool that constructs its own PATH will run whatever Python it found rather than the one pyenv selected. The nested shell case is the same problem one process deeper: a program that spawns a shell inherits an environment that may or may not still have the shim directory in front, so the interpreter inside that shell can differ from the one that started it.

Windows is declined outright, and WSL gives you Linux binaries under a virtual machine

The Windows section is the shortest and the most decisive in the README. pyenv does not officially support Windows and does not work in Windows outside the Windows Subsystem for Linux. Even there, the Pythons it installs are not native Windows versions but Linux versions running in a virtual machine, so you will not get Windows specific functionality.

The recommendation is a different project: the pyenv-win fork by @kirankotari, which does install native Windows Python versions.

So the honest answer to a Windows developer is that this tool is not for them, and that the workaround is a fork maintained by someone else. The consequence is a slower path and a different set of capabilities. A WSL Python is a Linux build, so anything that depends on native Windows paths, native file locking or a Windows specific extension is unavailable, and you are paying for a hypervisor layer to get a package manager that was not written for the platform. If your work depends on Windows native behaviour, the fork named in that section is the thing to evaluate, and you should read it as a different project with its own tradeoffs rather than as the same tool on another OS.

Two install routes, and only the git clone is something you can fork

On Linux and Unix the recommended route is one line:

bash
curl -fsSL https://pyenv.run | bash

The details of that installer live in a separate project, pyenv-installer. The second route is described as getting you going with the latest version and making it easy to fork and contribute any changes back upstream, which is a description of the git checkout:

bash
git clone https://github.com/pyenv/pyenv.git ~/.pyenv

There is an optional follow-up for speed, compiling a dynamic Bash extension, with the reassuring note that you need not worry if it fails and pyenv will still work normally:

bash
cd ~/.pyenv && src/configure && make -C src

The consequence is a real difference in what you end up with. The curl line pipes a moving script from a domain that is not github.com into your shell, which is fast and unrepeatable. The clone gives you a directory with a branch you can check out, diff and send patches from, at the cost of updating it yourself. The README also points out that the Homebrew route from the macOS section works on Linux too if you already have Homebrew, and that macOS developers can take the Linux routes instead.

The Makefile pins Bash 3.2 and 4.1, which is the real compatibility floor

The top level Makefile is the most precise statement of what pyenv supports. It pins `TEST_BASH_VERSIONS = 3.2.57 4.1.17` and `TEST_BATS_VERSION = v1.10.0`, and it builds Docker targets for each of those Bash versions plus a GNU variant of each, with the tag assembled as `bash-$(BASH)-gnu-$(GNU)`. A comment above one target reads: Run each unit test under bats docker.

Five image families are derived from those variables, named after the area under test: `test-unit-docker`, `test-python-build-docker`, `test-binary-docker`, `test-link-docker` and `test-pyenv-docker-image`, and a `test-docker` target depends on the first four.

Two things follow. Bash 3.2 is the oldest version in the matrix, and it is the version macOS ships as its system Bash, so the floor is set by that rather than by anything in the code. And a GNU variant of each version is tested separately, because a script that assumes a GNU builtin will fail on a stock Bash and the reverse is also true. The consequence for a contributor is that a shell construct available only in Bash 4 or only in GNU coreutils is not covered by the suite, and a change that uses one will pass locally on a modern machine and fail for someone on the old matrix.

Homebrew formulae can link against a pyenv Python, and the fix is a brew alias

The macOS section installs pyenv through Homebrew with `brew update` and `brew install pyenv`, or `brew install pyenv --head` for the latest development head rather than the latest release. After that come the post-installation steps: set up your shell environment, restart your shell, and install the Python build dependencies.

Then comes the part worth reading. Homebrew's `brew doctor` emits a warning that config scripts exist outside your system or Homebrew directories, and the README explains when that matters: if you are going to build Homebrew formulae from source that link against Python, such as Tkinter or NumPy. This is generally the case only if you develop such a formula, or if you are on an end of life macOS version where prebuilt bottles are no longer provided and you are using such a formula.

The consequence is a genuine linking hazard, because a formula compiled during an install can end up bound to a pyenv provided Python, and then break when you switch versions or remove one. The documented fix is a shell alias that strips the shims directory from PATH for brew only, with a Bash and Zsh form using `env PATH="${PATH//$(pyenv root)/shims:/}" brew` and a Fish form that does the same with `string replace`. It is a workaround in the literal sense: it hides pyenv from one command rather than fixing the underlying link.

plugins/ and pyenv.d/ are the two extension points, and COMMANDS.md is the reference

The top level of the repository shows how much of pyenv lives outside the executable. There is `bin/`, `libexec/` and `src/` for the code, `completions/` for shell completion, `man/` for manual pages, `test/` for the suite the Makefile drives, and then two directories that exist to be extended: `plugins/` and `pyenv.d/`.

The distinction between those two is worth knowing. `plugins/` holds separate repositories such as pyenv-virtualenv, the one the README recommends for environment management, which means the feature you are missing is usually a plugin someone else maintains. `pyenv.d/` is for shell code that runs around pyenv's own commands, which is where you put the hook that fires after an install. Alongside them sit COMMANDS.md, CHANGELOG.md, MAINTENANCE.md, CONDUCT.md, CONTRIBUTING.md and the LICENSE, plus a Makefile and a .vimrc.

The consequence is that the answer to how do I make pyenv do X is usually that X is a plugin, and the answer to how do I run something whenever pyenv installs a Python is `pyenv.d/`. The project's own cadence is regular patch releases, v2.8.6 on 2026-09-17, v2.8.5 on 2026-09-01 and v2.8.4 on 2026-08-13, on the master branch, with the last push on 2026-09-28.

Editorial conclusion

pyenv fits a Unix developer who needs several Python versions on one machine and is comfortable with PATH, shims and a Bash-level tool. It is a poor fit if you want one command to make an isolated environment, because that is the job it declines, and a poor fit on native Windows, which it declines for a documented reason. Before installing, read the Windows section if that is your platform, and on macOS read the Homebrew section about formula linking. If you plan to contribute, use the git checkout rather than the curl installer, because only the checkout is a repository you can fork.

Frequently asked questions

What is pyenv used for?

Switching between multiple Python versions. It changes the global Python version on a per-user basis, provides per-project Python versions, supports distributions including CPython, PyPy, Stackless Python and Jython, can be overridden with an environment variable, and can search for commands from multiple versions at once, which is useful for testing across versions with tox.

What's the difference between pyenv and venv?

pyenv states in its own README that it does not manage virtualenv. It selects which interpreter runs, using shims added to PATH, while virtualenv isolates packages inside one interpreter. The README suggests creating virtualenv yourself or using the pyenv-virtualenv plugin to automate it.

Which is better, pyenv or UV?

The repository does not mention uv. What it does state is that pyenv was made from pure shell scripts with no dependency on Python, that it is a fork of rbenv and ruby-build modified for Python, and that it works by adding a shim directory to PATH rather than by being loaded into your shell.

How do I use pyenv on windows?

It does not. The README says pyenv does not officially support Windows and does not work outside the Windows Subsystem for Linux, and that even in WSL the Pythons it installs are Linux versions in a virtual machine without Windows specific functionality. It recommends the pyenv-win fork, which installs native Windows versions.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
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.svg)](https://hysenlabs.com/projects/pyenv-pyenv)