Migrating from pyenv to mise — reading .python-version, handling pyenv-virtualenv markers, translating pyenv-installed versions, handling the compile-from-source situation, and tearing down ~/.pyenv cleanly. Use when a user is currently using pyenv and wants to adopt mise.
pyenv is the long-standing Python version manager. mise replaces it with the same underlying python-build backend (same compile path, same options) plus support for pre-built binaries and integration with every other language you use. Migration is usually painless.
MISE_PYTHON_COMPILE=0 — 10x faster installs.[env] and [tasks] — pyenv doesn't do either._.python.venv.mise.toml per project (or enable idiomatic reader).pyenv versions # installed Python versions
pyenv version # current version
pyenv global # global default
pyenv local # in-project local pin (reads .python-version)
grep -r pyenv ~/.zshrc ~/.bashrc ~/.bash_profile 2>/dev/null
ls .python-version 2>/dev/null # per-project version file
Also check for pyenv-virtualenv:
pyenv virtualenvs
cat .python-version # may contain a virtualenv name like "myproject-3.12.7"
pyenv-virtualenv is a plugin that lets .python-version contain a virtualenv name instead of a bare version. If your .python-version has something like myproject-3.12.7, you'll need to handle that separately (see "pyenv-virtualenv migration" below).
For a plain .python-version containing 3.12.7:
# mise.toml
[tools]
python = "3.12.7"
Or enable the idiomatic reader and let mise read .python-version directly:
# mise.toml or ~/.config/mise/config.toml
[settings]
idiomatic_version_file_enable_tools = ["python"]
For pre-built binaries (strongly recommended — much faster):
[env]
MISE_PYTHON_COMPILE = "0"
Or set it globally in your shell rc.
# ~/.config/mise/config.toml
[tools]
python = "3.12"
[env]
MISE_PYTHON_COMPILE = "0"
Remove pyenv's init:
# ~/.zshrc — DELETE
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init - zsh)"
eval "$(pyenv virtualenv-init -)" # if you had pyenv-virtualenv
Add mise:
eval "$(mise activate zsh)"
Restart shell.
which python # should be under ~/.local/share/mise/
python --version # matches mise.toml
cd <project>
python --version # matches the project's pin
If which python shows a pyenv path, the old PATH is sticky. Check for leftover pyenv lines, then open a fresh terminal.
pyenv-virtualenv stores .python-version with a venv name (e.g. myproject-3.12.7) and creates a venv under ~/.pyenv/versions/myproject-3.12.7/. mise doesn't have a direct equivalent, but _.python.venv is the idiomatic replacement:
.python-version contains: myproject-3.12.7
Venv lives in: ~/.pyenv/versions/myproject-3.12.7/
# mise.toml
[tools]
python = "3.12.7"
[env]
_.python.venv = { path = ".venv", create = true }
Venv lives in: .venv/ (in the project dir, gitignored)
.venv/ is in-project instead of centrally located. Pro: easier to blow away and recreate, no centralized cache to prune. Con: every project has its own full venv (but the Python interpreter itself is shared via mise's install dir, so this is less disk-heavy than it sounds).
If you want to keep a pyenv-virtualenv-style centralized layout:
[env]
_.python.venv = { path = "~/.cache/venvs/{{config_root | basename}}", create = true }
Not the idiomatic pattern, but possible.
rm -rf ~/.pyenv
# If installed via Homebrew:
brew uninstall pyenv pyenv-virtualenv
Restart the shell. Verify which pyenv reports not found.
You migrated to mise's compiled Python and the host is missing libssl-dev (or the equivalent). See mise-lang-python-overview → "Build dependencies". Or switch to pre-built: MISE_PYTHON_COMPILE=0 mise install python@<version>.
pip install fails with "externally-managed-environment"You're pip install-ing into the mise-installed Python's system site (PEP 668 error). Always use a venv:
python -m venv .venv
source .venv/bin/activate
pip install <pkg>
Or, better, use uv or poetry + _.python.venv to manage this for you. See mise-lang-python-packages.
python --version shows a different version than mise current pythonShim cache staleness. Run mise reshim and reopen the shell.
Point the IDE at the mise-installed interpreter explicitly:
~/.local/share/mise/installs/python/3.12.7/bin/python
Or the shim:
~/.local/share/mise/shims/python
The shim re-resolves on each invocation, so it always picks the project-correct version.
[tools]
python = "3.12 3.11 3.10" # three versions, 3.12 is default
mise installs and makes all three available. python3.11 --version works; bare python resolves to the first.
If you were using pip install --user black flake8 ruff or pipx, move to mise's pipx: backend:
# ~/.config/mise/config.toml
[tools]
python = "3.12"
"pipx:ruff" = "latest"
"pipx:black" = "latest"
"pipx:httpie" = "latest"
Version-pinned, managed, survives Python version upgrades (pipx detects and rebuilds).
Reinstall pyenv if needed (your .python-version files are untouched):
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
Remove mise activation from shell rc. Restart shell.
mise-lang-python-overview — Python version resolution.mise-lang-python-packages — uv / poetry / pipx decision.mise-env-directives — _.python.venv auto-activation.mise-migrate-from-asdf — general migration shape.mise.jdx.dev/faq.html#migrating-from-pyenv.github.com/astral-sh/python-build-standalone.