Python pyenv¶
- ID
pyenv- Home page
- 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 |
|
✓ |
|
|
||
|
||
|
✓ |
|
|
||
|
||
|
Selecting and configuring pyenv¶
Deselect pyenv for a single run with --no-pyenv, or persist the choice in your configuration:
[mpm]
pyenv = false
Keep it enabled but tune how mpm drives it with a per-manager override:
[mpm.managers.pyenv]
timeout = 900
mpm config-template pyenv prints every overridable attribute as a ready-to-paste block.
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 needs root.
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); --allow-unsupported-managers opts back in.
Version probe¶
The version is probed by running:
$ pyenv --version
pyenv 2.8.4
and extracted with:
r"^pyenv[ \t]+(?P<version>\d+(?:\.\d+)+)"
Reference traces¶
Raw native outputs captured in the bundled definition: the reference mpm’s parsers were written against. 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
Feed any of these through mpm and the raw output becomes one uniform table, the same shape for every manager: filter it, project columns, or export it (mpm --pyenv installed --output json, or csv, toml, yaml), each package carrying a purl and a version comparable across managers.
Changelog¶
7.7.0.dev0(unreleased)Add the Python pyenv version manager with
installed,search,installandremovesupport, shipped as a bundled definition. Listings go through--bare, which is what makes them independent of the working directory. Virtualenvs are excluded:pyenv uninstallreduces its argument to the part after the last slash, so removing one by the id the listing prints deletes a compatibility symlink and orphans the environment. It declares nooutdatedorupgrade, pyenv having no in-place interpreter upgrade.