Tooling#
The infrastructure that turns an installed Python into a working project. Virtual environments isolate dependencies per project; version managers keep multiple Pythons on the same host without collision; package managers resolve and install third-party code; package servers (PyPI and its mirrors) are where that code comes from. Each is a separate concern with its own defaults; together they are the operator’s daily toolchain.
Virtual environments#
A virtual environment is a per-project Python prefix. It isolates the project’s packages from the system Python and from every other project. The operator works in venvs by default; the only time a script touches system Python is to bootstrap into one.
venv, the stdlib module that ships with the interpreter.virtualenv, third-party predecessor; rarely needed now.uv venv, the Astral implementation, faster.conda/mamba, the data-science alternative; isolates binary packages, not just Python.
$ uv venv # creates .venv/
$ source .venv/bin/activate
$ uv pip install requests
$ python -m venv .venv # stdlib alternative
$ source .venv/bin/activate
$ pip install requests
The activation command varies by shell, .venv/bin/activate
for bash and zsh, .venv/bin/activate.fish for fish,
.venv/Scripts/activate.bat on Windows.
Activation is convenience, not requirement. ./venv/bin/python,
./venv/bin/pip, and uv run all work without sourcing the
activation script. Many CI configs skip activation and call the
binaries directly.
Version managers#
Multiple Python versions coexist on every operator host (one for
system tools, one for projects, sometimes one per project). A
version manager installs and switches between them without
touching /usr/bin/.
uv python, the Astral version manager. Installs to
~/.local/share/uv/python/.pyenv, classic shell-based version manager. Installs to
~/.pyenv/versions/.mise, multi-language version manager (also handles Node, Go, Rust toolchains).
asdf, predecessor to
mise.Distro packages (
apt install python3.12,dnf install python3.12). Works but ties the operator to whatever the distro ships.
$ uv python install 3.12
$ uv python list
$ pyenv install 3.12.4
$ pyenv global 3.12.4
$ pyenv local 3.11.9 # project-pin
The .python-version file pins a version per project.
pyenv, uv, and mise all read it.
Package managers#
The 2026 landscape and what each tool actually does.
Tool |
Role |
Notes |
|---|---|---|
|
All-in-one |
Install, resolve, lock, build, run scripts. The default
for new work. Two orders of magnitude faster than |
|
Installer |
Ships with Python. Reliable fallback when |
|
Project manager |
Common in established projects. Lockfile + builder + venv in one. |
|
Project manager |
PyPA-blessed; less adopted than |
|
CLI installer |
Installs command-line apps in isolated environments. Use
for global tools ( |
|
Binary manager |
Manages non-Python binary deps too (CUDA, MKL, GDAL). Common in data science and scientific Python. |
|
Lockfile generator |
For projects stuck on |
$ uv add requests
$ uv add --dev pytest
$ uv lock
$ uv sync
$ pip install --user ruff
$ pipx install httpie
Lock files pin every transitive dependency with hash digests so
uv sync (or poetry install, or pip install -r
requirements.txt --require-hashes) reproduces the exact
environment everywhere. Commit the lockfile. Audit it on every
bump.
uv.lock, written and read byuv.poetry.lock, the Poetry equivalent.requirements.txtpluspip-tools --generate-hashesfor projects onpip.
Package servers#
The package manager downloads from a package index. By default that index is the Python Package Index (PyPI). The operator’s choices when this default is wrong, when an internal mirror is in play, or when the registry itself is the threat.
PyPI, the public Python Package Index. Anyone can publish a package with any name.
TestPyPI, the staging registry. Used to verify a release before pushing to production PyPI.
devpi, self-hosted PyPI cache and private registry.
pip-savings.com / internal Artifactory / Nexus / AWS CodeArtifact / GCP Artifact Registry, enterprise package indexes that mirror PyPI and host private internal packages.
$ uv pip install --index-url https://internal.example/simple/ requests
$ pip install --index-url https://internal.example/simple/ requests
$ pip config set global.index-url https://internal.example/simple/
Warning
PyPI is a supply-chain attack surface. Anyone can publish a package with any name. The operator must assume the registry is adversarial.
Typosquatting,
rqeusts/request3/requests-instead ofrequests. Type the name once and copy from there.Dependency confusion, attacker publishes a public package with the same name as one of the operator’s private internal packages; the resolver picks the public one. Pin an index URL or vendor internal packages.
Compromised maintainers, legitimate package gets a malicious release pushed (event-stream, ua-parser-js, ctx, and others). Pin versions; review changelogs before bumping.
Post-install hooks,
setup.pyruns arbitrary Python during install, including in CI. Prefer wheel-only installs (--only-binary=:all:).
Mitigations.
Pin with hash digests in lockfiles.
Audit with
pip-audit,safety,trivy fs, orsnyk test.Sandbox untrusted packages, containers and fresh venvs, not the operator’s shell-init Python.
Mirror the indexes the operator depends on (devpi, Artifactory) to detach from public availability and pin the versions seen.
Other tooling#
Linters, formatters, type checkers, test runners, debuggers, and docs. These are not strictly part of the install / venv / package toolchain above, but every Python project reaches for them.
Concern |
Tool |
|---|---|
Linter |
|
Formatter |
ruff format (Black-compatible); Black classic. |
Type checker |
|
Tests |
pytest;
|
Debugger |
|
Docs |
|
Editor |
Pylance, ruff-lsp, python-lsp-server. |
Data-science tooling#
The toolchain pick up when the work is exploratory
analysis, model training, or anything that needs numpy /
pandas / scikit-learn / pytorch on real data.
Anaconda / Miniconda, Python distributions bundled with
conda. Anaconda ships ~250 pre-installed data-science packages; Miniconda ships onlycondaand the operator adds what they need.conda / mamba, package and environment managers that handle non-Python binary dependencies (CUDA, cuDNN, MKL, GDAL, hdf5, openCV). The standard choice when
pipcannot satisfy a heavy native dep cleanly.mambais a faster reimplementation in C++.Jupyter Notebook, the classic cell-based interactive document. Runs Python (and other kernels) inline with markdown, plots, and tables. The data scientist’s daily driver.
JupyterLab, the next-gen Jupyter UI, multi-document, file browser, terminal, text editor in one workspace.
IPython, the enhanced REPL that powers Jupyter kernels. Tab completion, magic commands (
%timeit,%run,%debug), object inspection.nbconvert, render notebooks to HTML, PDF, slides, Markdown.
papermill, parametrise and execute notebooks programmatically (CI-friendly notebook pipelines).
$ conda create -n analysis python=3.12 pandas numpy jupyter
$ conda activate analysis
$ jupyter notebook # classic UI
$ jupyter lab # JupyterLab UI
$ ipython # enhanced REPL
$ jupyter nbconvert notebook.ipynb --to html
Notebooks are great for exploration; they are bad for production
code. Promote anything stable into .py files under src/
and import from the notebook. Notebook-as-application is an
anti-pattern.
Common Tasks#
Create a fresh venv in the current directory.
$ uv venv
$ source .venv/bin/activate
Install a managed Python without touching system Python.
$ uv python install 3.12
$ uv python pin 3.12
Add a dependency and lock the project.
$ uv add requests
$ uv lock
Reproduce the locked environment exactly.
$ uv sync
Install a CLI tool globally without polluting any venv.
$ pipx install ruff
Point pip / uv at an internal package server.
$ pip config set global.index-url https://internal.example/simple/
$ uv pip install --index-url https://internal.example/simple/ requests
Audit installed packages for known CVEs.
$ uv pip install pip-audit
$ pip-audit
Verify a package’s hash before installing.
$ pip download requests --no-deps --no-binary=:all:
$ sha256sum requests-*.tar.gz