Python pyenv¶
- ID
pyenv- Links
- Upstream stars
⭐ 45,118
- Last commit
2026-09-28
- Version requirement
>= 2.3.13
- Platforms
🐧 Linux · 🍎 macOS
- Operations
installed·search·install·remove- purl types
pkg:pyenv/- CLI name
pyenv- Issues and PRs
- Source
pyenv is the Python version manager (https://github.com/pyenv/pyenv).
A package is one installed interpreter, named by the exact token pyenv prints,
which is also the token install and uninstall accept. The token is left
whole rather than split into an implementation and a version, because the
namespace is far too heterogeneous for one pattern to divide: 3.13.0,
3.13.0t, 3.13-dev, 3.14.0rc1, pypy3.11-7.3.20, graalpy-24.1.1,
miniconda3-latest and anaconda3-2024.10-1 all coexist, and any mis-split
would break the round-trip that makes the id useful.
Virtualenvs are deliberately excluded, which is what --skip-envs does, and
the reason is a bug rather than a preference. pyenv uninstall reduces its
argument to the part after the last slash, so handing back the very id the
listing prints for an environment, 3.12.11/envs/mango, reports success while
deleting the top-level mango compatibility symlink instead and orphaning the
environment itself. An mpm remove would destroy the wrong object and report
it as done. Environments are also created by a plugin’s pyenv virtualenv
rather than by pyenv install, so they were never addressable by half the
operations here.
Note
--bare is what makes the listing independent of the working directory.
pyenv’s default listing marks the selected interpreter with * and names
the .python-version file it was selected by, and that selection follows the
cwd up the directory tree. Only the marker moves: the set of rows is always
the contents of $PYENV_ROOT/versions. --bare drops the marker along with
the annotation, the --> alias expansion, and the system pseudo-entry that
is not an installable package. Upstream pins this with a test of its own, so
no equivalent of haxelib’s forced --global is needed here.
Note
The default listing also writes a warning to stderr when a .python-version
somewhere above the working directory names an interpreter that is not
installed, which is an ordinary state on a developer’s machine. --bare skips
that branch entirely, so nothing spurious reaches mpm’s diagnosis tail.
No outdated, and no upgrade of either kind: pyenv has no in-place upgrade,
a newer patch release being a separate install into its own prefix. Its
latest subcommand would have to be asked once per package, which a
definition cannot do, and it would need a comparison depth pyenv itself never
picks: given 3.12.11, nothing says whether the newer version is 3.12.15 or
3.14.7.
No sync: the catalog ships inside pyenv, so install --list is answered
offline and refreshing it means upgrading pyenv itself. No cleanup either:
rehash regenerates shims rather than reclaiming anything, and uninstall
already runs it, while the download cache has no command to purge it.
No escalation: pyenv installs under $PYENV_ROOT, defaulting to ~/.pyenv,
and its sources contain no privileged path. Its shell integration is not
required either, existing to put shims on $PATH rather than to make these
subcommands work.
Nothing here needs color suppression: the only code that emits escapes turns them off when its output is not a terminal, and it runs during an install.
What mpm adds to pyenv¶
Through mpm, pyenv gains --exact and --extended search, to narrow to exact names or match descriptions.
Bigger still, mpm reaches across every manager at once: mpm installed and mpm outdated cover pyenv alongside every other manager you run in one table, mpm upgrade --all updates them together, and mpm sbom exports the whole machine as one bill of materials.
Every mpm command also gains --dry-run and --plan previews, cross-scheme version comparison and purl identifiers. See manager augmentations for how each one is built.
Your pyenv commands, in mpm¶
You already know pyenv: each operation maps one-to-one onto mpm, in an interface shared by every manager.
To… |
With |
With |
|---|---|---|
List what’s installed |
|
|
Search for a package |
|
|
Install a package |
|
|
Remove a package |
|
|
Prefix any command above with --dry-run to simulate the underlying manager calls without touching the system: the safe way to watch what mpm would do before trusting it.
Operations¶
Operation |
Supported |
Notes |
|---|---|---|
|
✅ |
|
|
||
|
||
|
✅ |
Exact and extended search backfilled by |
|
✅ |
|
|
||
|
||
|
✅ |
|
|
||
|
||
|
Configuration¶
Ignore
pyenvon thempmCLI by passing the--no-pyenvoption.Ignore it for every run in your configuration:
[mpm] pyenv = false
Raise the timeout of all
pyenvcalls:[mpm.overrides.pyenv] timeout = 900
Run
mpm config-template pyenvto print all overridable settings for your configuration file:[mpm.overrides.pyenv] cli_names = [ "pyenv", ] cli_search_path = [] dry_run = false ignore_auto_updates = true plan = false post_args = [] pre_args = [] pre_cmds = [] requirement = ">=2.3.13" stop_on_error = false unmaintained = false version_cli_options = [ "--version", ] version_regexes = [ "^pyenv[ \\t]+(?P<version>\\d+(?:\\.\\d+)+)", ]
Recipes¶
A few jobs you would otherwise script around pyenv, one mpm command each:
Snapshot and clone a machine:
mpm --pyenv dump pyenv.toml, thenmpm restore pyenv.tomlon the next one.Export a compliance SBOM:
mpm --pyenv sbom(CycloneDX by default,--spdxfor SPDX).
Privilege escalation¶
mpm runs this manager as the current user and never prepends sudo by default. Flip the policy for its privileged operations with --sudo or the per-manager sudo override.
None of its operations is privileged.
See privilege escalation for the full policy.
Cooldown¶
State of Python pyenv’s release-age gating, from the cooldown support table:
Status: ❌ None
A cooldown only pays off where a compromised release can be withdrawn while the clock runs, and can only be emulated where the registry dates its releases. From the retraction table:
Registry: Python interpreter source releases
Retraction: None, and no operator to ask: each version is a build recipe shipped inside pyenv naming an upstream tarball, so withdrawing one means either the implementation pulling its own release or a new pyenv dropping the recipe
Publish date: ❌ a recipe names a source archive and its checksum, carrying no publication date for a release-age gate to read
With --cooldown set, mpm skips this manager’s install and upgrade operations rather than run them unguarded (fail-closed); --cooldown best-effort opts back in.
Reference traces¶
A collection of raw native outputs captured from the manager’s own CLI and recorded in the bundled definition. If you know Python pyenv well and a transcript below looks wrong, or a newer release changed its output format, report it.
$ pyenv versions --bare --skip-aliases --skip-envs
3.11.9
3.12.11
3.13.7
3.14.0rc1
graalpy-24.1.1
miniconda3-latest
pypy3.11-7.3.20
$ pyenv install --list
Available versions:
2.1.3
2.2.3
2.3.7
Version check¶
The version is probed by running:
$ pyenv --version
pyenv 2.8.4
and extracted with:
r"^pyenv[ \t]+(?P<version>\d+(?:\.\d+)+)"
Upstream project¶
Metrics |
|
|---|---|
Activity |
|
Popularity |
|
Metadata |
|
Changelog¶
8.0.0(2026-09-20)Add the pyenv Python version manager with
installed,search,installandremovesupport, virtualenvs excluded sincepyenv uninstallwould delete their base version instead.