For new Python projects in 2026, default to uv: it replaces pip, venv, virtualenv, pyenv, pip-tools, and most of pipx and Poetry in one Rust binary that runs 10-100x faster than the older tools and manages Python interpreters too. Keep existing Poetry or Pipenv projects on the tool they already use unless you have a concrete reason to migrate. Reach for conda or mamba only when you need non-Python native deps. Everything below is the layer underneath that summary: when each tool wins, the pyvenv.cfg internals all six tools share, the speed benchmarks, and step-by-step migration paths between every common pair. This guide is one of 10 explainers in our Python Environment Setup pillar page, which sits inside the broader learn Python complete guide.
Key Takeaways
uv is the 2026 default. Single Rust binary, 10-100x faster than pip, replaces five legacy tools, ships its own Python interpreter manager.
venv is still the no-install fallback. Ships with Python 3.3+, zero setup, works everywhere. Use it when you can't install anything else.
Poetry stays solid for existing projects. Mature lockfile, polished UX, healthy maintenance. Don't migrate just because uv is faster.
Pipenv is maintenance-mode. Don't pick it for new work. If you inherit a Pipenv project, migrate to uv, not Poetry.
pipx is in a different category. It's for installing Python CLI tools globally with isolation, not for project envs. uv now does this too with uv tool install.
Every tool writes the same pyvenv.cfg. Understand that file once and you understand all six.
Pick uv for new work, keep what works for old work, reach for conda when native scientific deps are involved.
What All Six Tools Have in Common: PEP 405
Every one of these tools, in the end, produces the same thing: a directory containing a Python binary (or a symlink to one), a site-packages folder, and a tiny config file called pyvenv.cfg. That format is standardized by PEP 405, accepted in 2011 and shipping in Python 3.3.
Crack open any virtual env you've made and you'll find something like this:
Whether you ran python -m venv .venv, virtualenv .venv, poetry install, or uv venv, the resulting directory speaks the same protocol. The home key tells Python where the base interpreter lives so the env can borrow the stdlib. The include-system-site-packages flag decides whether sys.path also picks up the system's site-packages after the env's own (the answer is almost always no, and that's the default).
This is why moving a venv between machines breaks it: the home path is absolute. It's also why the system Python you use to create the env matters: the env's interpreter borrows the stdlib from that exact install, and if you delete it later, the env stops working.
Understand pyvenv.cfg once and the rest is just packaging. The differences between these tools are about what they layer on top: dependency resolution, lockfiles, Python version management, and how fast they install wheels.
Tool 1: venv (stdlib, Python 3.3+)
The reference implementation. Ships with Python, zero install. Creates a PEP 405 env and that's it. Doesn't manage dependencies (you call pip inside the env), doesn't manage Python versions, doesn't have a lockfile.
Strengths. Always available, demands no third-party trust, and has the smallest possible footprint of any option on this list.
Weaknesses. There's no lockfile here (requirements.txt isn't a lockfile, it's an "install these packages" list), no dependency resolution beyond what pip does at install time, no interpreter management, and the install speed lags behind everything modern.
When it's right. Reach for stdlib venv in locked-down corporate environments where you can't install anything but Python, for short throwaway scripts where you don't want a config file, and for teaching: the first venv every Python learner should make is python -m venv. Reference: docs.python.org/3/library/venv.
Tool 2: virtualenv (third-party, predates venv)
The original. Predates PEP 405, then influenced it. Still maintained by PyPA. Same end result as venv with three real differences:
Works on Python 2.7 (venv does not).
Faster env creation thanks to wheel-bootstrapping a cached pip.
Better interpreter discovery: virtualenv -p 3.12 .venv finds Python 3.12 from PATH and uses it; with venv you call python3.12 -m venv .venv explicitly.
When it's right. Python 2 codebases that aren't dead yet, speed-sensitive CI on systems where uv isn't yet acceptable, and projects that need virtualenv-specific features like its bootstrap caching. Otherwise, skip it and use venv or uv.
Tool 3: Pipenv (maintenance mode)
Released 2017 by Kenneth Reitz as "pip + venv + dependency lockfile in one." It introduced Pipfile and Pipfile.lock and was briefly recommended by PyPA. Then development slowed, Poetry ate its lunch on UX, and uv ate everyone's lunch on speed. Pipenv still gets bug fixes; nobody recommends it for new work.
When it's right. You inherited a Pipenv project that works. Don't touch it until you have a concrete reason. When you do migrate, go straight to uv; don't take a detour through Poetry.
Tool 4: Poetry (still solid, 2017 onwards)
Poetry has been the de-facto standard for "I want a real lockfile and a publishing workflow" since around 2019. It uses pyproject.toml (which became standard via PEP 518 and PEP 621) and produces a deterministic poetry.lock.
poetry new myproject # scaffolds pyproject.toml + src layout
cd myproject
poetry add requests # resolves, writes lock, installs
poetry run python app.py # runs inside the managed env
poetry shell # drops you into the env's shell
Strengths. Poetry brings a mature lockfile, a polished CLI, group-aware deps via --group dev and --group test, built-in publishing to PyPI, and a healthy maintainer team behind it.
Weaknesses. It's slow next to uv (often 10x slower on cold installs), doesn't manage Python interpreters, and its resolver has historically struggled on large dep graphs, though that situation is steadily improving with each release.
When it's right. Keep existing Poetry projects on Poetry; the migration churn rarely pays for itself. New projects work fine on Poetry when the team already knows it and doesn't want to learn another tool. Library publishing to PyPI is also a sweet spot, since Poetry's poetry publish workflow is already well-trod by thousands of packages.
Tool 5: pipx (different category)
pipx is not a project env tool. It's for installing Python CLI applications globally with each app isolated in its own private venv. Think black, ruff, httpie, poetry itself.
pipx install black # black goes on PATH, isolated env
pipx install ruff
pipx install httpie
pipx list # shows installed tools
pipx upgrade-all
The pipx team explicitly says this in their comparisons doc: pipx is for "installing and running" Python applications, not for managing project dependencies. It's the right tool for your global tooling layer. It's the wrong tool for the requirements.txt of your app.
2026 note. uv now does this too with uv tool install black, with the same isolation guarantees and much faster install times. New users can skip pipx and just use uv. If you already use pipx, there's no urgency to switch.
Tool 6: uv (the 2026 default)
uv is built by Astral, the team behind Ruff. It's written in Rust, ships as one binary (no Python bootstrap needed), and collapses pip + venv + virtualenv + pyenv + pip-tools + most of pipx and Poetry into a single CLI.
uv init myproject # scaffolds pyproject.toml + .python-version
cd myproject
uv python install 3.13 # installs Python 3.13 (no system Python needed)
uv add requests httpx # resolves, locks, installs
uv run app.py # runs inside the managed env, no activation needed
uv tool install black # global tool install with isolation
uv pip install requests # drop-in pip replacement when you need it
The three sources of uv's speedup.
Rust. No Python startup tax. The resolver runs as compiled code, not interpreted.
Global content-addressed cache. Once uv has downloaded and extracted requests-2.31.0-py3-none-any.whl, every future install in any project hardlinks from the cache instead of re-downloading. Cache hits can take milliseconds.
Parallel metadata fetching. The resolver speculatively fetches metadata for many candidate versions in parallel, so the network round-trip stops being the bottleneck.
Weaknesses. uv is still young (1.x as of this writing) and some workflows need a fallback: uv pip install exists explicitly for the "I just want pip behavior, faster" path. The team also iterates fast, which means occasional behavior changes between releases, and the compatibility doc lists every known difference from pip.
When it's right. uv is the default for new projects, a drop-in upgrade for pip in CI where the 10x cold-install speedup pays for itself instantly, a replacement for pyenv when you also want a faster pip, and a replacement for pipx when you also want managed project envs alongside global tools.
Side-By-Side Comparison Matrix
Capability
venv
virtualenv
Pipenv
Poetry
pipx
uv
Creates virtual envs
Yes
Yes
Yes
Yes
Yes (per-tool)
Yes
Dependency resolver
No (uses pip)
No (uses pip)
Yes
Yes
No (per-tool)
Yes
Deterministic lockfile
No
No
Yes (Pipfile.lock)
Yes (poetry.lock)
No
Yes (uv.lock)
Manages Python versions
No
No
No
No
No
Yes
Installs global CLI tools
No
No
No
No
Yes
Yes (uv tool)
Build & publish to PyPI
No
No
No
Yes
No
Yes (uv publish)
Cold install speed
Slow
Slow
Slow
Slow
Slow
10-100x faster
Project status (2026)
Stable (stdlib)
Stable (PyPA)
Maintenance
Active
Active
Active, fast-moving
Speed Benchmarks: Cold Install of 50 Dependencies
Numbers vary by network, machine, and exact dep list. The relative shape is what's stable. On a typical laptop installing a fresh app with ~50 transitive deps (requests, fastapi, sqlalchemy, pytest, ruff, and friends):
Tool
Cold install (no cache)
Warm install (cache hit)
Lock generation
pip install -r requirements.txt
~30-50s
~15s
n/a
poetry install
~40-80s
~20s
~10-30s
pipenv install
~50-90s
~25s
~30-60s
uv sync
~2-5s
<1s
~1-3s
The cache hit row is where uv pulls farthest ahead: the global content-addressed cache means a second project sharing 90% of its deps with the first installs almost instantly. In CI this compounds: a uv-based pipeline often spends more time spinning up the runner than installing Python deps.
Migration Paths
requirements.txt to uv
uv init # creates pyproject.toml
uv add -r requirements.txt # imports every line
uv sync # resolves and locks
# Now you have pyproject.toml + uv.lock.
# Delete requirements.txt when CI is migrated.
Poetry to uv
# Option 1: uv reads pyproject.toml directly (Poetry's format is compatible
# for [project] but not for [tool.poetry]; convert the table).
# Use the official migration helper:
uvx migrate-to-uv
# Option 2: manual
# - Move [tool.poetry.dependencies] to [project] dependencies array
# - Move [tool.poetry.group.dev.dependencies] to [dependency-groups] dev
# - Run: uv sync
# Pipfile to requirements export
pipenv requirements > requirements.txt
pipenv requirements --dev > requirements-dev.txt
# Then run the requirements.txt to uv path above:
uv init
uv add -r requirements.txt
uv add --dev -r requirements-dev.txt
uv sync
conda to uv (when you don't need native deps)
conda list --export > spec.txt # export current env
# Hand-pick lines that are pure-Python: most data science wheels on PyPI work
# Edit spec.txt down to a clean list, then:
uv init
uv add $(cat spec.txt | cut -d= -f1 | grep -v '^#' | tr '\n' ' ')
uv sync
If you need CUDA-linked PyTorch, GDAL, or other native-heavy scientific deps, conda still wins. uv has been catching up via the PyTorch index integration, but conda's binary-channel model is hard to beat for non-Python C/C++ stacks.
When uv Is NOT the Right Choice
Four legitimate reasons to skip uv:
Conda lane. CUDA-linked PyTorch, full GDAL/PROJ stack, R interop, anything where the binary needs to be conda-built. Use conda/mamba.
Locked-down environment. If you can't install anything that didn't come with Python, you can't install uv. Use python -m venv.
Existing tooling is fine. If your team uses Poetry and the CI is green, "uv is faster" alone isn't enough to migrate. The churn cost is real.
Lockfile portability concerns.uv.lock is uv-specific. If you need a lockfile that other tools also understand (some compliance scanners only read poetry.lock or requirements.txt), check that first.
Activation: What That source Command Actually Does
"Activating" a venv is a two-line shell trick:
Prepend .venv/bin (or .venv\Scripts on Windows) to $PATH, so python and pip point at the env's binaries.
Save the old $PATH in $_OLD_VIRTUAL_PATH so deactivate can restore it.
That's literally what source .venv/bin/activate does. You can verify by reading the file: it's around 80 lines of shell. Modern tools mostly skip activation: uv run app.py and poetry run app.py set the PATH for the duration of one command. This is faster and avoids the "I forgot which env I'm in" bug.
The pyproject.toml Standard
All modern tools (Poetry, uv, hatch, flit, pdm) write to pyproject.toml. The [project] table is standardized by PEP 621, so dep declarations move between tools mostly unchanged:
[project]
name = "myapp"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"requests>=2.31",
"fastapi>=0.110",
]
[dependency-groups]
dev = [
"pytest>=8",
"ruff>=0.5",
]
A file like that is readable by uv, Poetry (with minor adjustments for the [tool.poetry] section), pdm, and hatch. The lockfiles aren't portable (uv.lock vs poetry.lock vs pdm.lock), but the source-of-truth dep list is. That makes future migration cheap: change tools without rewriting your package definition.
Real-World Recommendations By Scenario
Scenario
Pick
Why
New Python web app, fresh team
uv
Fast, modern, manages Python too
Quick throwaway script, can't install tools
venv
Stdlib, no install required
Existing Poetry project, CI green
Poetry
No migration churn warranted
Inherited Pipenv project
Migrate to uv when convenient
Pipenv is maintenance mode
Open source library publishing to PyPI
uv or Poetry
Both have publish; uv is faster
Data science with CUDA PyTorch + GDAL
conda or mamba
Binary channel for native deps
Installing black + ruff + httpie globally
uv tool or pipx
Isolated, on PATH
CI pipeline, install speed matters
uv
10-100x speedup vs pip
Teaching beginners
venv first, uv second
Learn the stdlib basics, then the fast tool
Frequently Asked Questions
Which Python environment tool should I use in 2026?
For new projects in 2026, use uv. It replaces pip, venv, virtualenv, pyenv, pip-tools, and most of what pipx and Poetry do, all in a single Rust binary that runs 10-100x faster than the older tools and manages Python interpreters too. Stick with what you have if a project already uses Poetry or Pipenv and works fine; migration is easy when you decide to. Use conda or mamba only when you need non-Python native deps that pip can't handle, like CUDA-linked builds of scientific libraries.
What is the difference between venv and virtualenv?
venv is the stdlib module (since Python 3.3, PEP 405); virtualenv is the third-party package that predates it and still works on Python 2 and across more Python versions. They solve the same problem in nearly the same way. Differences worth knowing: virtualenv is faster to create environments (it caches the bootstrap), supports older Pythons, and has a richer CLI for picking interpreters. For Python 3.3+, venv is the default no-install choice unless you specifically need virtualenv's extra features.
Is uv really 10-100x faster than pip?
Yes, in real-world workloads. The speedup comes from three sources: Rust instead of pure Python, a global content-addressed cache that avoids re-downloading or re-extracting wheels you've installed before in any project, and a parallel resolver that fetches metadata for many candidates at once. On cold installs of a fresh requirements file with 50+ deps the speedup is often 10-30x; on warm installs hitting the cache, it can hit 100x. For a one-line install of a single package, the wall-clock difference is hundreds of milliseconds, which matters in CI.
Is Pipenv dead?
Not dead, but maintenance-mode. Pipenv still gets bug fixes and works for existing projects. New projects in 2026 should not choose it: Poetry has a richer feature set, and uv is faster and consolidates more tools. If you inherit a Pipenv project, leave it alone unless you have a specific reason to migrate, then move directly to uv rather than Poetry.
What is pyvenv.cfg and why does it matter?
pyvenv.cfg is a plain-text file written into every Python virtual environment (the spec is PEP 405). It holds two key facts: home, which points at the directory of the base Python install the env was created from, and include-system-site-packages, which decides whether the system site-packages is also on sys.path. Every tool that creates virtual environments (venv, virtualenv, uv, Poetry, Pipenv) ends up writing a pyvenv.cfg with these same fields. Understanding it once explains why activation works, why moving an env between machines breaks it, and why the path to the base Python matters.
The Bottom Line: One Tool to Default To, the Rest On Purpose
The decision is simpler than the tool count suggests. New work in 2026: uv. Existing work that's healthy: leave it. Conda lane: conda. Locked-down systems: venv. Everything else is detail. The PEP 405 layer underneath all of them is the same, which is why migration is fast when you decide to do it. Once you understand pyvenv.cfg you can read any virtual env any tool produced and know exactly what it'll do. For deeper dives, see fixing ModuleNotFoundError after pip install, running multiple Pythons on one machine, and upgrading all pip packages safely, or browse the full Python Environment Setup pillar page.
Skip the Setup Headaches Entirely
CodeGym's Python track runs Python in the browser through its WebIDE. No venv, no pip, no PATH conflicts. The AI validator checks every submission in seconds; 800+ hands-on tasks across 62 levels build muscle memory while you focus on code, not configuration. First level free; full plan on the pricing page.
Start learning Python (free) →
GO TO FULL VERSION