Jesse A. Green / notebook

pyenv Administrator & Developer Guide

Generated June 2026 from pyenv/pyenv at 4518092

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the operational and architectural reference for pyenv — the shell-based Python version manager. It is written for the people who deploy pyenv across machines and CI, harden and tune Python builds, automate provisioning, write plugins, or need to understand exactly what every line of pyenv init does to a shell.

For installation and day-to-day command usage, see the companion User Guide. This document goes deeper: the shim execution model, the hook system, the version-resolution pipeline, the complete build-customization surface, security considerations, and the project's internal structure.


Table of Contents


Executive Summary

pyenv is a Python interpreter version manager implemented entirely as POSIX shell scripts. It has no compiled core (aside from one optional Bash builtin for speed) and no runtime dependency on Python itself — a deliberate design choice that avoids any bootstrap problem.

Its central mechanism is the shim: a directory of lightweight executables placed at the front of PATH that intercept invocations of python, pip, and every command provided by an installed Python, then redirect them to the version selected by configuration. Selection is layered — shell session, project directory, then global user default — and resolved fresh on every command.

Attribute Value
Language Bash / POSIX shell (one optional C Bash builtin in src/)
Runtime dependencies A POSIX shell; readline or GNU coreutils; git for checkout installs
Build dependencies C compiler + Python build libraries (for pyenv install)
Supported platforms Linux, macOS, WSL, other UNIX-like systems (no native Windows)
Supported shells Bash, Zsh, Fish, ksh/ksh93/mksh, PowerShell, Nushell
Distribution Git repo, pyenv.run installer, Homebrew, MacPorts
License MIT
Latest release (this snapshot) v2.7.1
Lineage Forked from rbenv / ruby-build

Note

Because pyenv is "pure shell," its attack surface, performance profile, and failure modes are those of shell scripts and the build toolchain — not of a long-running daemon. There is no service to monitor, no port to open, and no privileged process.


Architecture

The dispatcher model

pyenv is a thin front-end (libexec/pyenv) that delegates to subcommands by name, exactly like git. When you run pyenv install, the dispatcher locates an executable named pyenv-install on PATH and execs it.

The dispatcher's startup sequence (libexec/pyenv):

  1. Handle --debug by exporting PYENV_DEBUG=1 and enabling set -x with an informative PS4.
  2. Resolve its own install prefix via a realpath/readlink abs_dirname (preferring the optional realpath Bash builtin in src/ when present, else GNU readlink).
  3. Normalize PYENV_ROOT (default ~/.pyenv) and PYENV_DIR (default $PWD).
  4. Prepend every plugins/*/bin directory and libexec to PATH so subcommands and plugin commands are discoverable.
  5. Build PYENV_HOOK_PATH from a fixed set of locations plus per-plugin etc/pyenv.d directories.
  6. Dispatch on the first argument: empty prints version+help, -v/--version runs pyenv---version, -h/--help runs pyenv-help, otherwise exec pyenv-<command> with the remaining args (a trailing --help is rerouted to pyenv-help <command>).
pyenv install 3.12.4
  │
  ├─ libexec/pyenv               (dispatcher: sets PYENV_ROOT/PATH/HOOK_PATH)
  └─ exec pyenv-install 3.12.4   (resolved from plugins/python-build/bin)
        └─ python-build …        (downloads, configures, compiles, installs)

Each subcommand is a standalone script carrying a # Summary: and # Usage: header comment (surfaced by pyenv help) and a --complete branch that prints its valid completions/flags (consumed by the shell completion scripts).

The shim mechanism

The shim is the heart of pyenv. After pyenv init places $(pyenv root)/shims at the front of PATH, every Python-provided command name in that directory shadows the real one.

A shim is a copy of this prototype shim (generated by pyenv-rehash):

#!/usr/bin/env bash
set -e
[ -n "$PYENV_DEBUG" ] && set -x

program="${0##*/}"

export PYENV_ROOT="…"
SHIM_PATH=${0%/*}
if [[ $SHIM_PATH != "$PYENV_ROOT/shims" ]]; then
  export _PYENV_SHIM_PATH="$SHIM_PATH"
fi
exec "$(command -v pyenv)" exec "$program" "$@"

Every shim is byte-for-byte identical except for its filename. When invoked, the shim simply re-execs pyenv exec <its-own-name> <args>. pyenv exec then resolves the active version, prepends that version's bin/ to PATH, sets PYENV_VERSION, and runs the real executable.

Important

Because shims are identical copies, pyenv detects an upgrade by comparing the first existing shim against the freshly generated prototype. If they differ, all shims are deleted and regenerated. This is why a pyenv upgrade transparently refreshes the shim directory on the next rehash.

The rehash pipeline

pyenv-rehash regenerates the shim directory to match the set of executables across all installed versions. Its design handles concurrency and partial failures carefully:

  1. Locking. It uses the shell's noclobber option to atomically create $(pyenv root)/shims/.pyenv-shim as a lock. Only one rehash runs at a time. A lock file older than 10 minutes is treated as stale and removed. The wait loop honors PYENV_REHASH_TIMEOUT (default 60s).
  2. Writability probe. Because the Linux Landlock sandbox can make [ -w ] lie (kernel ≥ 6.14.0), rehash also probes writability by attempting a real mktemp inside the shims directory.
  3. Prototype creation. It writes the prototype shim shown above.
  4. Upgrade detection. remove_outdated_shims wipes the directory if the existing shims differ from the new prototype.
  5. Registration. make_shims $(pyenv-versions --executables) registers a shim name for every executable in every installed version. Bash ≥ 4 uses an associative array; Bash 3 falls back to a space-delimited string.
  6. Plugin hooks. Every script returned by pyenv-hooks rehash is sourced, letting plugins register or suppress shims (see The hook system).
  7. Install & prune. Registered shims are created (hard-link-style copies of the prototype), then any unregistered file in the directory is removed.

Note

The bundled pyenv.d/rehash/conda.bash hook overrides make_shims to skip creating shims for Anaconda-provided system binaries (e.g. openssl, curl) so they don't shadow the user's real tools. pyenv.d/rehash/source.bash rewrites selected shims so that sourced scripts (rather than executed binaries) work correctly.

The hook system

Hooks are the official extension point. The dispatcher assembles PYENV_HOOK_PATH at startup from, in order:

  1. Any pre-existing PYENV_HOOK_PATH value.
  2. $(pyenv root)/pyenv.d.
  3. pyenv's own bundled pyenv.d (unless pyenv was cloned directly into PYENV_ROOT).
  4. System paths: /usr/etc/pyenv.d, /usr/local/etc/pyenv.d, /etc/pyenv.d, /usr/lib/pyenv/hooks.
  5. Each $(pyenv root)/plugins/*/etc/pyenv.d.

A hook is a .bash file under <hook-path>/<command>/. When a command runs pyenv-hooks <command>, it collects every matching hook script; the command then sources them at a defined point. The bundled hook directories are:

Hook point Bundled hooks Effect
rehash conda.bash, source.bash Adjust which shims get generated.
exec pip-rehash.bash (+ pip-rehash/ wrappers) Auto-rehash after pip/easy_install/conda install commands so new console scripts get shims immediately.
install latest.bash Resolve latest-style version requests at install time.

Tip

Use pyenv hooks <command> to enumerate exactly which hook scripts are active for a command on a given machine — invaluable when a plugin changes behavior unexpectedly.

Shell integration internals

pyenv init - is the only pyenv command that injects code into your interactive shell. Running eval "$(pyenv init - <shell>)" performs these steps (see libexec/pyenv-init):

  1. Detect shell (skipped if you pass the shell name — recommended for speed). Detection reads /proc/$PPID/cmdline or ps to identify the parent shell.
  2. Create dirs — mkdir -p $(pyenv root)/{shims,versions}.
  3. Set up the shims path — prepend $(pyenv root)/shims to PATH, first stripping any existing occurrence so repeated init calls don't duplicate entries. --no-push-path guards this with a "skip if already present" check instead.
  4. Export PYENV_SHELL so subcommands know the active shell.
  5. Source completions for the detected shell from completions/pyenv.<shell> if readable.
  6. Emit a rehash (command pyenv rehash) unless --no-rehash is given.
  7. Define the pyenv shell function, which intercepts the state-mutating subcommands (those returned by pyenv commands --sh, e.g. shell, rehash) and evals their output in the current shell, delegating everything else to the real pyenv binary.
eval "$(pyenv init - bash)"      → full integration (shims + function + completion + rehash)
eval "$(pyenv init --path)"      → shims path + rehash only, no shell function/completion
pyenv init --install [shell]     → append the setup lines to the shell's startup files
pyenv init --detect-shell        → print detected shell + profile/rc file paths

Important

The shell function is what makes pyenv shell, which must mutate the calling shell's PYENV_VERSION, possible. Without integration, only the non-mutating subcommands work; pyenv emits shell integration not enabled for pyenv shell.

pyenv init --install writes to per-shell startup files. For POSIX shells it targets both the rc file (interactive) and the login profile (~/.bash_profile if present else ~/.profile for Bash; ~/.zprofile for Zsh). It refuses to edit any file that already contains the string pyenv (case-insensitive), printing guidance to apply changes manually — a guard against duplicate or conflicting configuration.

Version resolution pipeline

On each shim invocation, pyenv exec (through pyenv-version-name/pyenv-version-file) determines the active version from the first source that resolves:

1. $PYENV_VERSION                         (set by `pyenv shell`)
2. ./.python-version                      (current directory)
3. ../.python-version, ../../…            (walk up to filesystem root)
4. $(pyenv root)/version                  (global; absent ⇒ "system")

Multiple whitespace/newline-separated versions in any source are all activated, searched left-to-right; system is always implicitly appended last (fall-through). Prefix auto-resolution applies to all subcommands except uninstall: install resolves a prefix to the latest known version, other commands to the latest installed one. A legacy .pyenv-version file is still honored but a sibling .python-version takes precedence.


Repository Structure

pyenv/
├── bin/                     Thin entry stubs (e.g. `pyenv` on PATH)
├── libexec/                 Core subcommands — one script per `pyenv-<cmd>`
│   ├── pyenv                Dispatcher
│   ├── pyenv-init           Shell integration generator
│   ├── pyenv-rehash         Shim regeneration
│   ├── pyenv-exec           Run a command under the selected version
│   ├── pyenv-version*       Resolution + reporting commands
│   ├── pyenv-global/-local  Selection persistence
│   ├── pyenv-sh-shell       Session selection (shell-function backed)
│   └── …                    which, whence, prefix, root, latest, hooks, shims, commands, completions, help
├── plugins/
│   └── python-build/        The build engine behind `pyenv install`
│       ├── bin/             pyenv-install, pyenv-uninstall, python-build
│       ├── share/python-build/   Build definitions (one file per version) + patches
│       └── scripts/         Maintenance helpers (add_cpython.py, add_miniconda.py, …)
├── pyenv.d/                 Bundled hooks (exec/, rehash/, install/)
├── completions/             pyenv.bash, pyenv.zsh, pyenv.fish, pyenv.pwsh
├── man/man1/pyenv.1         Man page
├── src/                     Optional C `realpath` Bash builtin (speed-up)
├── test/                    Bats test suite + Dockerfile
├── COMMANDS.md              Command reference
├── CHANGELOG.md             Release history
└── README.md                Primary documentation

Note

The bulk of the 1,500+ files in the repo are build definitions and patches under plugins/python-build/share/python-build/. Each installable Python version is a small shell definition file; per-version patches live in patches/<version>/. The executable surface — the part that does the work — is the ~25 scripts in libexec/ plus python-build.


System Requirements

Component Requirement Notes
Shell POSIX-compatible Bash 3.2+ supported; Bash ≥ 4 enables associative-array fast paths in rehash.
Path resolution readline/GNU coreutils or the compiled src/realpath builtin Set PYENV_NATIVE_EXT to force the builtin and fail loudly if unavailable.
Version control git For checkout installs and pyenv update.
Build toolchain C compiler + dev headers OpenSSL, zlib, readline, SQLite, bzip2, libffi, etc. See Suggested build environment.
Optional download accelerator aria2 Used automatically if aria2c is on PATH.
Optional checksum tools shasum / openssl / sha256sum Enable automatic SHA-2 verification of downloads.

Filesystem Layout

Everything pyenv manages lives under the pyenv root (PYENV_ROOT, default ~/.pyenv):

Path Purpose
$(pyenv root)/versions/<name>/ One directory per installed Python. The directory name is the version name.
$(pyenv root)/shims/ Generated shim executables (front of PATH).
$(pyenv root)/version Global version selection file.
$(pyenv root)/shims/.pyenv-shim Rehash lock file.
$(pyenv root)/cache/ Download cache (if it exists; or PYTHON_BUILD_CACHE_PATH).
$(pyenv root)/sources/ Kept source trees when pyenv install --keep is used.
$(pyenv root)/plugins/<name>/ Installed plugins.
$(pyenv root)/pyenv.d/ User hook scripts.
./.python-version Per-directory selection (project-committable).

Tip

Because version names are just directory names, you can rename or symlink versions, or back up versions/ directly. A version "exists" to pyenv if and only if its directory is present.


Configuration Reference

Runtime environment variables

These influence pyenv's behavior at command time.

Variable Default Description
PYENV_VERSION — Active version for the current shell session. Set/cleared by pyenv shell.
PYENV_ROOT ~/.pyenv Root directory for versions, shims, cache, plugins.
PYENV_DIR $PWD Directory where .python-version resolution begins.
PYENV_DEBUG — Enables set -x shell tracing. Equivalent to pyenv --debug <cmd>.
PYENV_HOOK_PATH (assembled) Colon-separated hook search path (see hooks).
PYENV_SHELL (set by init) The shell pyenv was initialized for.
PYENV_REHASH_TIMEOUT 60 Seconds pyenv rehash waits for its lock before giving up.
PYENV_NATIVE_EXT — If set, require the compiled realpath builtin; abort if it can't load.
_PYENV_INSTALL_PREFIX (computed) Internal — pyenv's own install directory.
_PYENV_SHIM_PATH (set by shim) Internal — set when a shim is invoked from outside the canonical shims dir.

Build customization (python-build)

These variables tune pyenv install / python-build. They are the primary lever for hardening, optimizing, mirroring, and offline-provisioning Python builds.

Build locations & caching

Variable Description
TMPDIR Where temporary build files go.
PYTHON_BUILD_BUILD_PATH Where sources are downloaded and built (default: a subdir of TMPDIR). Set this on noexec-restricted machines.
PYTHON_BUILD_CACHE_PATH Directory for caching downloaded archives (default ~/.pyenv/cache if present). Pre-seed it to provision offline.
PYTHON_BUILD_ROOT Override the location of build definitions in share/python-build/.
PYTHON_BUILD_DEFINITIONS Colon-separated extra paths searched for build definitions.

Download & mirrors

Variable Description
PYTHON_BUILD_MIRROR_URL Override the mirror root. URLs are formed as <mirror>/<sha2>.
PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM Don't append the checksum to mirror URLs (for pull-through caches like Artifactory).
PYTHON_BUILD_SKIP_MIRROR Always download from the original source URL, never the mirror.
PYTHON_BUILD_HTTP_CLIENT Force aria2, curl, or wget (default: that search order).
PYTHON_BUILD_CURL_OPTS / PYTHON_BUILD_WGET_OPTS / PYTHON_BUILD_ARIA2_OPTS Extra args to the chosen HTTP client.

Dependency ecosystems (macOS/BSD)

Variable Description
PYTHON_BUILD_USE_HOMEBREW / PYTHON_BUILD_SKIP_HOMEBREW Force-enable / disable Homebrew dependency discovery.
PYTHON_BUILD_USE_MACPORTS / PYTHON_BUILD_SKIP_MACPORTS Force-enable / disable MacPorts discovery.
PYTHON_BUILD_HOMEBREW_OPENSSL_FORMULA Override the Homebrew OpenSSL formula.
PYTHON_BUILD_MACPORTS_OPENSSL_FORMULA Override the MacPorts OpenSSL formula (default openssl3 openssl).
PYTHON_BUILD_TCLTK_FORMULA Override the Homebrew Tcl/Tk formula.

Compiler & make

Variable Description
CC Path to the C compiler.
CONFIGURE_OPTS Extra options for ./configure.
MAKE Override the make command (e.g. gmake).
MAKE_OPTS / MAKEOPTS Extra options for make.
MAKE_INSTALL_OPTS Extra options for make install.
PROFILE_TASK Override the PGO profiling task (with --enable-optimizations).
<PACKAGE>_CFLAGS / _CPPFLAGS / _LDFLAGS Per-package compiler flags (e.g. PYTHON_CFLAGS, READLINE_LDFLAGS).
<PACKAGE>_CONFIGURE_OPTS / _MAKE_OPTS / _MAKE_INSTALL_OPTS / _MAKE_INSTALL_TARGET Per-package configure/make controls.

Note

Default build behavior aims for least surprise but sets a few non-default options: --enable-shared is on (required to embed CPython and matches the official Docker image), --with-universal-archs defaults to universal2 on Apple Silicon, and MACOSX_DEPLOYMENT_TARGET defaults to the running macOS version. Pass overrides via CONFIGURE_OPTS/PYTHON_CONFIGURE_OPTS.


Deployment & Provisioning

Reproducible install on a fleet of machines

Pin pyenv to an exact tag and a known PYENV_ROOT:

export PYENV_ROOT=/opt/pyenv
git clone --branch v2.7.1 --depth 1 https://github.com/pyenv/pyenv.git "$PYENV_ROOT"

Then provision shells non-interactively. For automation that must not edit dotfiles, prefer --path and avoid the interactive --install:

export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"

Docker / CI

The recommended pattern is to pin a version at build time and put shims on PATH:

ENV PYENV_ROOT=/opt/pyenv
ENV PATH=$PYENV_ROOT/shims:$PYENV_ROOT/bin:$PATH
RUN git clone --branch v2.7.1 --depth 1 https://github.com/pyenv/pyenv.git $PYENV_ROOT \
 && pyenv install 3.12.4 \
 && pyenv global 3.12.4 \
 && pyenv rehash

Tip

For CI agents that need to switch Python mid-job, point BASH_ENV at a file that runs eval "$(pyenv init - bash)" so each step gets shims. Mind the BASH_ENV/.bashrc loop warning when doing so.

Offline / air-gapped builds

  1. Pre-seed PYTHON_BUILD_CACHE_PATH (e.g. mkdir -p $PYENV_ROOT/cache) and copy source archives there under the filename pyenv reports in its "Downloading …" message.
  2. Or stand up an internal mirror and set PYTHON_BUILD_MIRROR_URL (with PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM for pull-through caches).

Operations

Task Command
Verify install pyenv --version
List installed versions pyenv versions
Install a version pyenv install <version> (-v for full build log)
Force-rebuild a broken version pyenv install -f <version>
Remove a version pyenv uninstall -f <version>
Rebuild shims pyenv rehash
Inspect resolution pyenv version / pyenv version-origin
Inspect a command's target pyenv which <cmd>
List active hooks pyenv hooks <command>
Trace a command pyenv --debug <command> …
Update pyenv pyenv update (plugin) or git -C $(pyenv root) pull

Health checks

# Confirm shims are first on PATH
echo "$PATH" | tr ':' '\n' | grep -n shims

# Confirm a selected version resolves correctly
pyenv which python

# Confirm a freshly pip-installed tool got a shim
pip install pytest && pyenv rehash && pyenv which pytest

Security

pyenv has no network service and runs with the user's own privileges. The meaningful security considerations are around how Python is obtained and built.

Caution

Curl-to-shell installation. curl -fsSL https://pyenv.run | bash executes a remote script. For controlled environments, prefer a pinned git checkout (--branch <tag>) or vendor the installer and review it, rather than piping a live URL into a shell.

Concern Mitigation
Download integrity python-build verifies the SHA-2 checksum of each archive automatically when shasum/openssl/sha256sum is available. Bundled definitions all carry checksums. Keep a checksum tool installed so verification is never silently skipped.
Mirror trust The default mirror is GitHub Pages, with fallback to the official source URL. If you set PYTHON_BUILD_MIRROR_URL, ensure the mirror is trusted; only set PYTHON_BUILD_MIRROR_URL_SKIP_CHECKSUM when you understand it disables URL-embedded checksum matching.
Patch application --patch reads from stdin and applies to source before build. Review patch sources, especially when fetched over HTTP.
PATH precedence Shims sit at the front of PATH. A malicious package that installs a command name (e.g. a fake python) would get a shim like any other. Audit what you pip install.
Startup-file edits pyenv init --install edits shell startup files but refuses files already containing pyenv config — review diffs in managed environments, or use --path in automation to avoid dotfile edits entirely.
Shim PATH leakage Shims add the selected version's bin/ to child PATH; some downstream tools observe internal paths (#3436). Use pyenv exec scoping or unset PYENV_VERSION for nested shells where this matters.

Writing Plugins

A pyenv plugin is a directory under $(pyenv root)/plugins/<name>/ that mirrors pyenv's own layout. The mapping is simple: anything pyenv looks for under $(pyenv root)/<path> it also looks for under $(pyenv root)/plugins/<name>/<path>.

Put under the plugin… …to provide
bin/pyenv-<command> A new pyenv <command> subcommand (appears in pyenv commands).
etc/pyenv.d/<hook-point>/*.bash A hook that runs at exec, rehash, install, which, version-name, or version-origin.
share/python-build/<name> A custom build definition for pyenv install <name>.

Authoring a subcommand. Make bin/pyenv-foo executable, add a # Summary:/# Usage: header so pyenv help foo works, and handle a --complete first argument to feed shell completion.

Authoring a hook. A hook is a Bash fragment sourced by the host command — it shares the command's variables and functions and can override them (as conda.bash overrides make_shims). To affect shell state from a hook, it must run within a sh-* command path that the shell function evals.

See Authoring plugins on the wiki for the full hook-point catalog.


Build System & Testing

pyenv's test suite uses Bats, driven by a Makefile.

Make target Runs
make test Whole suite on the local host.
make test-docker Whole suite in Docker.
make test-unit Unit tests only.
make test-plugin python-build plugin tests.
make test-unit-docker-<BASH_VERSION> Unit tests under an official Bash container at a pinned version.
make test-unit-docker-gnu-<BASH_VERSION> …plus GNU tools layered in.

Filter tests with Bats environment variables:

BATS_TEST_FILTER=".*installed.*" BATS_FILE_FILTER="build.bats" make test-plugin
build.bats
 ✓ yaml is installed for python
 ✓ homebrew is used in Linux if Pyenv is installed with Homebrew
 ✓ homebrew is not used in Linux if Pyenv is not installed with Homebrew

3 tests, 0 failures

Run the raw suite directly with Bats:

bats test
bats test/<file>.bats

Note

The optional src/ C extension provides a realpath Bash builtin. Build it with src/configure && make -C src; it is a pure performance optimization and pyenv falls back to GNU readlink if it is absent. Tests run across multiple Bash versions (including 3.2) because pyenv deliberately supports old Bash.


Troubleshooting (Operational)

Symptom: rehash intermittently fails to acquire its lock

Cause: A crashed rehash left .pyenv-shim, or storage is slow. (#3457) Diagnosis: ls -la $(pyenv root)/shims/.pyenv-shim; check its age. Resolution: Stale locks older than 10 minutes are auto-removed; force with rm -f $(pyenv root)/shims/.pyenv-shim. Raise PYENV_REHASH_TIMEOUT on slow filesystems. Keep shims on local storage.

Symptom: shims dir reports unwritable on a sandboxed host

Cause: Landlock (Linux ≥ 6.14.0) can make [ -w ] report incorrectly. pyenv compensates with a mktemp probe, but a genuinely read-only shims/ still fails. Resolution: Ensure the user owns and can write $(pyenv root)/shims; review Landlock/AppArmor policy for the directory.

Symptom: build fails with a missing-module error after a successful compile

Cause: A development header (OpenSSL, SQLite, expat, libffi) was absent at build time, so CPython built without that module. (#3449) Resolution: Install the dev package, then pyenv install -f <version>. Unset a misleading LD_LIBRARY_PATH. Consult Common build problems.

Symptom: pyenv init causes an infinite loop or slow shells

Cause: BASH_ENV pointing at .bashrc (#264), or repeated detection/rehash at startup (#2918). Resolution: Put eval "$(pyenv init - bash)" in .bash_profile, not .bashrc, on such systems. Always name the shell, and add --no-rehash to the init line, rehashing manually after installs.


Potential Improvements

Drawn from the project's own open issue tracker, these are areas an operator or contributor may want to track or address:

  • Prebuilt CPython distribution (#2334) — would eliminate the build-toolchain requirement for common platforms; today every source release compiles locally.
  • XDG base-directory compliance (#1789) — PYENV_ROOT currently centralizes config+data+cache under one tree rather than following $XDG_CONFIG_HOME/$XDG_CACHE_HOME.
  • Shim startup latency (#2802, #2375) — each shim invocation re-execs pyenv; a hot-path optimization would reduce per-command overhead, especially on macOS.
  • Signature verification of CPython artifacts (#3087) — current verification is SHA-2 checksum based; Sigstore verification would strengthen supply-chain assurance.
  • Sandbox/Landlock writability detection — the mktemp probe is a workaround for a kernel access() quirk; a cleaner errno-aware check would be more robust.

Note

These reflect open community requests at the documented commit and are not commitments by the project. Verify current status on the issue tracker before relying on any of them.


Quick Reference

Selection precedence (highest first): PYENV_VERSION → ./.python-version → parent .python-version → $(pyenv root)/version → system.

Init modes: pyenv init - (full) · pyenv init --path (shims only) · pyenv init --install (edit dotfiles) · pyenv init --detect-shell.

Key paths: versions $(pyenv root)/versions/<name>/ · shims $(pyenv root)/shims/ · global file $(pyenv root)/version · cache $(pyenv root)/cache/.

Most-used commands:

Command Does
pyenv install -v <v> Build & install a version with full log
pyenv global / local / shell <v> Set selection at each scope
pyenv versions / version List / show active
pyenv rehash Rebuild shims
pyenv which <cmd> Resolve a shim
pyenv exec <cmd> Run under selected version, no shims
pyenv hooks <cmd> List active hooks
pyenv --debug <cmd> Trace execution

Build tuning quick hits: PYTHON_CONFIGURE_OPTS='--enable-optimizations --with-lto' for speed · PYTHON_BUILD_CACHE_PATH for caching · PYTHON_BUILD_MIRROR_URL for an internal mirror · CONFIGURE_OPTS/CC/MAKE_OPTS for toolchain control.