Python has a packaging problem that is not really a packaging problem — it is a too many tools problem. If you have ever wondered whether to stick with venv and pip, switch to uv vs Poetry, or just give up and install everything globally (please do not), this guide is for you. You will set up all three the right way, see an honest comparison, and walk away with a clear answer for your next project.
Here is the short version for the impatient: for a brand-new Python project in 2026, uv is the default choice. It is one fast binary that replaces pip, venv, pip-tools, pipx, and (mostly) pyenv. But venv plus pip is still the correct zero-dependency baseline, and Poetry remains a solid choice for teams already running on it. Let us get into the details so you can make the call with evidence, not hype.
Last verified: 2 October 2026. Commands and workflows below were checked against the official uv and Poetry documentation on the date of publication.
The short answer: which one should you pick?
If you only read one section, read this one.
- Starting a new project? Use uv.
- Joining a project that already uses Poetry and works? Keep Poetry. Do not migrate for no reason.
- Writing a quick script or learning Python?
python -m venvplus pip is all you need. - Need CUDA, GDAL, or other non-Python native libraries? Use pixi or conda for the environment, then let uv handle the Python packages inside it.
- Publishing a library to PyPI with a polished release workflow? Poetry’s build and publish tooling is still the most mature.
This matches the decision flow recommended by the Python packaging community in 2026, including the pydevtools package manager guide: uv is the default, and you only deviate when you have a concrete reason.
How Python environments actually work (the 60-second version)
A virtual environment is an isolated folder containing its own Python interpreter and its own set of installed packages. It exists so that Project A’s dependencies never conflict with Project B’s. When you “activate” an environment, your shell just points python and pip at that folder.
A lockfile records the exact resolved version of every dependency and sub-dependency. Your pyproject.toml says “I need requests”, but the lockfile says “requests 2.32.3, with certifi 2024.7.4, urllib3 2.2.2…” — which is what makes builds reproducible across machines and in CI.
The three approaches differ in how much of this they handle for you:
- venv + pip: creates the isolated folder, installs packages. No resolver lockfile — you pin versions yourself with
pip freeze. - uv: creates the folder, resolves and locks dependencies (
uv.lock), installs Python versions, runs commands inside the environment — one tool. - Poetry: the same all-in-one idea as uv (lockfile, venv management, build, publish), but written in Python and significantly slower at resolution.
Option 1: venv + pip — the baseline, done right
venv ships with Python itself, so there is nothing to install. It is the right choice for tutorials, one-off scripts, and any situation where adding a third-party tool would be overkill. Here is the correct workflow:
# Create the environment in a .venv folder
python -m venv .venv
# Activate it (macOS / Linux)
source .venv/bin/activate
# Activate it (Windows PowerShell)
# .venv\Scripts\Activate.ps1
# Upgrade pip inside the environment first — stale pip causes weird failures
python -m pip install --upgrade pip
# Install what you need
pip install requests httpx
# Freeze exact versions for reproducibility
pip freeze > requirements.txt
To recreate the environment on another machine:
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
The honest limitation: requirements.txt is a snapshot, not a resolved lockfile. It does not record which packages depend on which, and pip installs one package at a time without solving the whole dependency graph up front. For a script with five dependencies that is fine. For a service with fifty, you will feel it.
Option 2: uv — the 2026 default
uv is a Python package and project manager written in Rust by Astral, the team behind the Ruff linter. Per Astral’s own benchmarks it resolves and installs dependencies 10–100x faster than pip, using a global content-addressed cache with hardlinks so environments on disk stay small. One binary replaces pip, pip-tools, virtualenv, pipx, and mostly pyenv.
Install uv
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# macOS via Homebrew
brew install uv
# Windows (PowerShell)
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# Or from PyPI into your current Python
pip install uv
uv --version
Start a project
uv init myapp
cd myapp
This creates a pyproject.toml following the PEP 621 standard, a .venv folder, and a hello-world main.py. If the Python version your project needs is not installed, uv downloads and installs it automatically — no pyenv required.
The daily workflow
# Add a dependency (updates pyproject.toml and uv.lock)
uv add requests
# Add dev-only dependencies
uv add --dev pytest ruff
# Run your code — no manual activation needed, uv uses the project venv
uv run main.py
uv run pytest
# Reproduce the exact locked environment (this is your CI command)
uv sync
# Pin or install a Python version
uv python install 3.12
uv venv --python 3.12
The uv extras worth knowing
# Run a CLI tool without installing it globally (replaces pipx)
uvx ruff check .
# Install a CLI tool in an isolated environment
uv tool install ruff
# Run a single script with declared dependencies — no project needed
uv run --with requests script.py
# Use uv as a drop-in faster pip inside an existing venv workflow
uv pip install -r requirements.txt
Commit uv.lock to version control. It is a cross-platform lockfile, so the same lock reproduces on Linux, macOS, and Windows. Your CI command is uv sync and it finishes in seconds, not minutes.
Option 3: Poetry — the established all-in-one
Poetry brought modern dependency management to Python years before uv existed, and it deserves the credit: lockfiles, automatic venvs, and build/publish in one CLI. If your team already runs on Poetry and it works, the pragmatic advice is to stay — migrating a healthy project buys you little.
Install Poetry
# Official installer (recommended — isolated from your system Python)
curl -sSL https://install.python-poetry.org | python3 -
# Or via pipx
pipx install poetry
poetry --version
Start a project
# Scaffold a new project
poetry new myapp
cd myapp
# Or initialise inside an existing directory
poetry init
The daily workflow
# Add a dependency (updates pyproject.toml and poetry.lock)
poetry add httpx
# Add dev dependencies
poetry add --group dev pytest ruff
# Install everything from the lockfile (your CI command)
poetry install
# Skip dev dependencies in production
poetry install --without dev
# Run commands inside the project venv
poetry run pytest
# Enter the venv shell
poetry shell
# Create the venv inside the project folder (easier for editors to find)
poetry config virtualenvs.in-project true
# Use a specific interpreter
poetry env use python3.12
poetry env info
Two things to know about modern Poetry. First, Poetry 2.0 (released January 2025) adopted the standard PEP 621 [project] table in pyproject.toml, so new Poetry projects and uv projects now speak the same metadata language — before 2.0, everything lived in a Poetry-specific [tool.poetry] section. Second, Poetry’s dependency resolver is Python-based and can take minutes on large trees, which is the single biggest practical complaint against it and the main reason new projects choose uv.
Where Poetry still leads: publishing. poetry build and poetry publish (with poetry publish --build doing both) remain the most polished release workflow in the ecosystem. uv has uv publish, but Poetry’s is more mature.
Head-to-head: uv vs Poetry
| venv + pip | uv | Poetry | |
|---|---|---|---|
| Install | Built into Python | One binary | Installer script or pipx |
| Lockfile | No (manual pip freeze) |
uv.lock, cross-platform |
poetry.lock |
| Venv management | Manual | Automatic | Automatic |
| Python version management | No | Yes (uv python) |
No (needs pyenv) |
| Speed | Slow | Very fast (Rust) | Moderate (Python-based) |
PEP 621 [project] |
N/A | Yes, native | Yes, since 2.0 |
| Build + publish to PyPI | Via twine | Yes (uv build, uv publish) |
Yes, most mature |
| Monorepo workspaces | No | Native | Limited (path deps) |
| Run tools without installing | No | uvx |
No |
| Maturity | Ancient, stable | Newer, fast-moving | Established |
On speed specifically: independent benchmarks, including an open-source package-manager benchmark from OpenTeams, consistently show uv installing and locking dramatically faster than both pip and Poetry on real-world dependency trees. One widely cited community test on a mid-size FastAPI project measured cold installs of ~47s (pip), ~38s (Poetry), and ~4s (uv). The exact numbers vary by machine and cache state, but the ordering does not.
When to choose which: a decision guide
Work through these in order:
- Is it a script, a tutorial, or a learning exercise? Use venv + pip. Zero new tools, zero concepts to learn.
- Is it a new real project? Use uv. Fastest installs, automatic Python management, standard lockfile,
uv runmeans you never think about activation. - Does it need CUDA, GDAL, or other non-PyPI native libraries? Keep a conda/pixi environment for the native layer, then use uv inside it for the Python packages. This hybrid is what many data-science teams actually run.
- Is the project already on Poetry and healthy? Stay on Poetry. “Slow resolver” is not a reason to churn a working build pipeline — optimise when it hurts.
- Are you publishing a library where release ergonomics matter most? Poetry’s build/publish flow is still the smoothest, though uv is closing the gap.
- Do you depend on a Poetry plugin with no uv equivalent? Stay on Poetry until that changes.
Migrating to uv
From a Poetry project, the community migration tool works well. It reads your existing pyproject.toml (and poetry.lock) and converts it to uv’s format:
cd my-poetry-project
# Convert pyproject.toml from Poetry format to uv format
uvx migrate-to-uv
# Regenerate the lockfile and sync the environment
uv lock
uv sync
From a pip + requirements.txt project, you have two paths. The gradual one keeps your workflow and just swaps the installer:
uv pip install -r requirements.txt
The full one converts you to a locked uv project: create a pyproject.toml with uv init --bare, then uv add your top-level dependencies (not every pinned transitive line — let the resolver do its job) and uv sync.
After migrating, run your full test suite before committing. Resolution differences between tools can surface genuinely conflicting pins that the old tool was silently tolerating.
Mistakes to avoid
- Mixing tools in one project. Pick one manager per project. Running pip inside a Poetry venv or Poetry inside a uv venv is the number-one source of “works on my machine” pain.
- Not committing the lockfile.
uv.lockandpoetry.lockbelong in version control.requirements.txttoo. The one exception is libraries, where you commit the lockfile for your own CI but keep dependency ranges loose for consumers. - Installing packages globally. If your shell’s
pip listis longer than your arm, stop. Useuvx/uv tool install(or pipx) for CLI tools you want on your PATH. - Forgetting
python -m pip install --upgrade pipin fresh venvs. The pip bundled with older Pythons can fail on modern wheels in confusing ways. - Pinning Python versions you do not test. If your
pyproject.tomlsaysrequires-python = "==3.13", your CI should actually run 3.13.
Further Reading & References
- uv official documentation — the complete guide, from installation to workspaces and publishing.
- Poetry official documentation — installation, dependency management, and publishing reference.
- Which Python package manager should I use? (pydevtools) — a maintained decision tree for 2026, including when to reach for pixi instead.
- UV vs Poetry: Which Python Package Manager Should You Use? (bswen) — a practical comparison based on real project usage.
- uv Is Quietly Killing Every Other Python Package Manager — speed measurements and the “one tool, three commands” workflow.
- UV: A Faster, All-in-One Python Package Manager (Corey Schafer) — video walkthrough comparing uv with the classic pip + venv workflow.
- Python UV Tutorial: A Faster Replacement for Pip, Virtualenvs & Poetry — hands-on video covering install,
uv init,uv add,uv sync, and cache management. - How can I migrate from Poetry to uv? (Stack Overflow) — community answers on the Poetry-to-uv migration path.



