From mise-toolkit
Python package managers — uv vs poetry vs pipx vs pip. The rule is "mise pins python, uv/poetry manages project venv, pipx manages global CLIs". Covers the uv-first workflow (the current recommended default) and why mixing tools is the
npx claudepluginhub ray-manaloto/claude-code-marketplace --plugin mise-toolkitThis skill uses the workspace's default tool permissions.
Python's package management has been a mess for a decade. The good news: **uv** (by Astral, same folks as ruff) has become the clear winner for new projects, and the rules are finally simple.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Checks Next.js compilation errors using a running Turbopack dev server after code edits. Fixes actionable issues before reporting complete. Replaces `next build`.
Guides code writing, review, and refactoring with Karpathy-inspired rules to avoid overcomplication, ensure simplicity, surgical changes, and verifiable success criteria.
Share bugs, ideas, or general feedback.
Python's package management has been a mess for a decade. The good news: uv (by Astral, same folks as ruff) has become the clear winner for new projects, and the rules are finally simple.
mise pins the Python interpreter
└─> uv manages the project's venv and lockfile
└─> installed packages in .venv/
And separately, for global CLIs:
mise
└─> pipx:<tool> installs global Python CLIs in isolated venvs
Rule: mise handles "which Python". uv handles "which packages, in this project". pipx handles "which Python CLIs, globally". Don't cross the streams.
| You want to… | Use |
|---|---|
| Start a new Python project | uv |
| Join a poetry-using project | poetry (don't migrate for no reason) |
| Install a global Python CLI (ruff, black, httpie, jupyterlab) | pipx (via mise's pipx: backend) |
| Run a single script with deps | uv (uv run --with requests script.py) |
| Rails-style "just make it work" for a small script | uv |
| Scientific computing with conda-style env | uv (supports requirements.txt style) or pixi (if you need bioconda etc.) |
The 2026 default is uv. It replaces pip, pip-tools, virtualenv, pyenv (no, mise), pipx (partially), and poetry. It's 10-100x faster than pip for installs. The project lock format is stable.
[tools]
python = "3.12"
"pipx:uv" = "latest" # install uv itself via mise
[env]
MISE_PYTHON_COMPILE = "0"
_.python.venv = { path = ".venv", create = true }
[tasks.install]
run = "uv sync"
sources = ["pyproject.toml", "uv.lock"]
[tasks.test]
depends = ["install"]
run = "uv run pytest"
[tasks.lint]
depends = ["install"]
run = "uv run ruff check ."
[tasks.fmt]
depends = ["install"]
run = "uv run ruff format ."
pyproject.toml:
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.110",
"pydantic>=2.0",
]
[dependency-groups]
dev = [
"pytest>=8.0",
"ruff>=0.6",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
uv sync reads this, creates/updates .venv, writes uv.lock. uv run pytest executes pytest inside the venv. Clean, fast, deterministic.
If you're joining a poetry project, don't migrate to uv just for the sake of it. Poetry works fine with mise:
[tools]
python = "3.12"
"pipx:poetry" = "1.8"
[env]
MISE_PYTHON_COMPILE = "0"
[tasks.install]
run = "poetry install"
[tasks.test]
run = "poetry run pytest"
Poetry manages its own venv (usually in ~/Library/Caches/pypoetry/virtualenvs/ or .venv if you set poetry config virtualenvs.in-project true). mise pins the Python that poetry uses to create that venv.
Don't use both _.python.venv and poetry — poetry creates its own venv and gets confused.
[tools]
python = "3.12"
"pipx:ruff" = "latest"
"pipx:httpie" = "latest"
"pipx:jupyterlab" = "latest"
"pipx:black" = "24.10"
Each pipx: entry installs the package into its own isolated venv under mise's install dir. The CLI is shimmed onto PATH. Same interface as a normal pipx install, but version-pinned and mise-lock'd.
Use pipx for: global tools (ruff, black, httpie, awscli, jupyterlab). Don't use pipx for: project dependencies (those go in pyproject.toml).
pip still exists and still works. Use it when:
pip install ... and you're in a venv.Don't use pip install globally (outside a venv). macOS and Ubuntu now refuse by default (PEP 668 "externally-managed-environment").
pyproject.toml semantics.pip install into the mise-installed Python system site. Always use a venv.sudo pip install anything, ever.uv can install and manage Python itself (uv python install 3.12). This overlaps with mise. Current guidance:
Don't use uv python install when mise is already managing Python. They'd both try to own the install dir.
For single scripts, uv has a magic feature — inline dep metadata:
#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["httpx", "rich"]
# ///
import httpx
from rich import print
print(httpx.get("https://httpbin.org/ip").json())
uv run script.py creates an ephemeral venv with the listed deps and runs the script. Replaces half of the "just pip install requests" tutorials.
source .venv/bin/activate in mise tasks — use uv run / poetry run / _.python.venv auto-activation instead.pip install -r requirements.txt in a CI workflow with no lockfile — non-reproducible.pipx install and pip install --user interchangeably — pipx is strictly better for CLIs..venv/ — no. .gitignore it.conda inside a mise project — pick one.mise-lang-python-overview — Python version resolution and compile flags.mise-migrate-from-pyenv — moving off pyenv.mise-env-directives — _.python.venv = { path, create } details.docs.astral.sh/uv/.peps.python.org/pep-0668/.