Python Cheatsheet
Virtual Environments and pip
Use this Python reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Create and Activate a venv
A virtual environment is a directory holding its own interpreter symlink,
site-packages, and scripts. It isolates one project's dependencies from every
other project and from the system Python.
python3 -m venv .venv # create, uses the python that ran it python3 -m venv --upgrade-deps .venv # also upgrade the bundled pip python3 -m venv --system-site-packages .venv # also see system packages python3 -m venv --clear .venv # wipe and recreate in place python3 -m venv --prompt myapp .venv # custom name in the shell prompt
| Shell | Activate | Deactivate |
|---|---|---|
| bash · zsh (macOS, Linux) | source .venv/bin/activate | deactivate |
| fish | source .venv/bin/activate.fish | deactivate |
| PowerShell | .venv\Scripts\Activate.ps1 | deactivate |
| cmd.exe | .venv\Scripts\activate.bat | deactivate |
.venv/bin/python -m pip install requests # no activation needed, always worksActivation only prepends .venv/bin to PATH and sets VIRTUAL_ENV. It is a
convenience, not a requirement, so scripts and CI should call the absolute
interpreter path instead.
Never commit
.venv. It contains compiled, platform-specific binaries, it is hundreds of megabytes, and it breaks on any other OS or Python version. Add.venv/to.gitignoreand commit the dependency file instead. Name it.venv(the convention every editor anduvauto-detects) and keep it inside the project.
pip install
Always python -m pip, never bare pip. Bare pip resolves through PATH and
can be a different environment's script, which installs into the wrong place with
no error.
| Command | Effect |
|---|---|
python -m pip install requests | latest compatible release |
python -m pip install "django>=5.0,<6" | version specifiers, quote them |
python -m pip install "requests==2.32.3" | exact pin |
python -m pip install -r requirements.txt | everything in a file |
python -m pip install -e . | editable install of the current project |
python -m pip install -U requests | upgrade (--upgrade) |
python -m pip install --no-deps pkg | skip dependencies, for manual pinning |
python -m pip install --dry-run pkg | resolve and report, install nothing |
python -m pip install "uvicorn[standard]" | with an extras group |
python -m pip install --index-url URL pkg | replace PyPI entirely |
python -m pip install --extra-index-url URL pkg | add a second index |
python -m pip install --only-binary :all: pkg | refuse source builds |
python -m pip install --no-cache-dir pkg | ignore the wheel cache |
python -m pip install --user pkg | into the per-user dir, not a venv |
# From version control, a local path, or a URL python -m pip install "git+https://github.com/psf/requests.git@v2.32.3" python -m pip install "git+https://github.com/org/repo.git@main#egg=pkg" python -m pip install "pkg @ git+https://github.com/org/repo.git" python -m pip install ./local/wheel-1.0-py3-none-any.whl python -m pip install -e "./libs/mylib[dev]"
pip install -e . installs the project in the current directory in editable
mode: it points site-packages at your source tree with a .pth file, so code
edits take effect immediately with no reinstall. It needs a pyproject.toml (or a
legacy setup.py) in that directory. Adding, renaming, or removing a package
still needs a reinstall, and console-script entry points are only regenerated on
reinstall. This is how you make import mypkg work from tests without touching
PYTHONPATH.
Inspect, Upgrade, Remove
python -m pip list # everything installed python -m pip list --outdated # what has a newer release python -m pip list --not-required # top-level only, not pulled in as a dep python -m pip show requests # version, location, Requires, Required-by python -m pip show -f requests # plus every installed file python -m pip check # report broken or conflicting deps python -m pip uninstall requests # -y to skip the prompt python -m pip cache dir; python -m pip cache info python -m pip cache purge # delete the whole wheel cache python -m pip download -d ./wheels -r requirements.txt # fetch, do not install python -m pip install --no-index --find-links ./wheels -r requirements.txt python -m pip config list # where settings come from python -m pip --version # also prints the python it belongs to
pip uninstall does not remove dependencies that are now orphaned, and pip has
no autoremove. pip also has no built-in "upgrade everything" command, on
purpose. Recreate the environment from a pinned file instead.
requirements.txt vs pyproject.toml
Two different jobs. pyproject.toml declares what your project needs.
requirements.txt records what a working environment contains.
python -m pip freeze > requirements.txt
freeze limitations, all of which bite in practice:
- It flattens direct and transitive dependencies into one list, so you lose which ones you actually asked for. - It is one machine's resolution. Platform-specific and Python-version-specific wheels get pinned as if they were universal. - It records no hashes, so the install is not verifiable. - It emits editable and VCS installs in forms that may not reinstall cleanly, and it includes anything you installed by hand.
# requirements.txt, the full grammar requests==2.32.3 django>=5.0,<6.0 uvicorn[standard]==0.30.6 numpy==2.1.1 ; python_version >= "3.10" # environment marker pywin32==306 ; sys_platform == "win32" -r requirements-base.txt # include another file -e . # the project itself --index-url https://pypi.org/simple # options are allowed inline torch==2.4.1 --hash=sha256:abc... # requires --require-hashes
The layered pattern: hand-write loose inputs, generate a fully pinned lock.
python -m pip install pip-tools pip-compile requirements.in -o requirements.txt # resolve and pin pip-compile --upgrade-package django requirements.in # bump just one pip-compile --generate-hashes requirements.in # verifiable pip-sync requirements.txt # install AND remove, exact match uv pip compile requirements.in -o requirements.txt # same job, much faster
pyproject.toml is the standard project metadata file (PEP 621) and the one that
makes pip install -e . and python -m build work.
[project] name = "myapp" version = "0.1.0" requires-python = ">=3.11" dependencies = ["requests>=2.32", "pydantic>=2.8"] [project.optional-dependencies] dev = ["pytest>=8", "mypy>=1.11", "ruff"] [project.scripts] myapp = "myapp.cli:main" # creates a `myapp` command on install [build-system] requires = ["hatchling"] build-backend = "hatchling.build"
python -m pip install -e ".[dev]" # project plus the dev extra
Use pyproject.toml for a library or an installable app, and a lock file for a
deployed service. A tiny script folder can get by with only requirements.txt.
Build and Publish
python -m pip install build twine python -m build # writes dist/*.whl and dist/*.tar.gz python -m twine check dist/* # validate metadata and the README render python -m twine upload --repository testpypi dist/* # rehearse here first python -m twine upload dist/* # real PyPI
Authenticate with an API token (username __token__) or, for a public repo,
PyPI trusted publishing from CI so no token exists at all. A version can never be
re-uploaded, so bump the version for every fix.
The Tooling Landscape
| Tool | Scope | Lock file | Notes |
|---|---|---|---|
venv + pip | env + install | no (use pip-tools) | stdlib, zero install, the baseline |
uv | env, install, lock, run, Python versions | uv.lock | Rust, 10-100× faster, drop-in pip interface |
pipx | install CLI apps | no | one isolated env per tool, not for project deps |
poetry | env, install, lock, build, publish | poetry.lock | opinionated, own CLI, its own resolver |
pdm | env, install, lock, build | pdm.lock | PEP 621 native, supports PEP 582 local packages |
conda · mamba | env, install, non-Python deps | environment.yml | ships CUDA, MKL, compilers. Scientific stacks |
pyenv | interpreter versions only | no | pairs with any of the above |
hatch | env matrix, build, publish | no | build backend plus env manager |
# uv, the command mapping most people need uv venv # create .venv (add --python 3.13) uv pip install -r requirements.txt # pip-compatible, into .venv uv pip compile requirements.in -o requirements.txt uv add requests # edit pyproject.toml + update uv.lock + install uv remove requests uv lock # resolve to uv.lock uv sync # make .venv match uv.lock exactly uv sync --frozen # CI: install from the lock, never re-resolve uv run pytest # run in the project env, syncing first uv run --with rich python # ephemeral extra dependency uvx ruff check . # run a tool without installing it (uv tool run) uv tool install ruff # persistent CLI install, like pipx uv python install 3.13 # manage interpreters, like pyenv uv python list
# pipx, for command-line tools you want on PATH but not in your project pipx install ruff pipx install "httpie==3.2.4" pipx inject poetry poetry-plugin-export # add a dep to a tool's own env pipx run cowsay hello # one-off, nothing installed pipx list; pipx upgrade-all; pipx uninstall ruff
# poetry poetry new myapp; poetry init # scaffold or adopt poetry add requests; poetry add --group dev pytest poetry install; poetry install --no-root poetry lock; poetry update poetry run pytest; poetry shell poetry build; poetry publish poetry env info --path # where the env actually lives # pdm pdm init; pdm add requests; pdm install; pdm run pytest; pdm lock # conda, only when you need compiled non-Python dependencies conda create -n myapp python=3.13 numpy conda activate myapp; conda deactivate conda env export > environment.yml conda env create -f environment.yml conda install -c conda-forge polars # pyenv, interpreter versions pyenv install 3.13.6; pyenv local 3.13.6; pyenv versions
Pick one per project and commit its lock file. uv is the default choice for
new work, conda when the dependency chain includes CUDA or Fortran, venv plus
pip-tools when you want nothing but the standard library plus one dev tool. Never
mix poetry install and pip install in the same environment, the lock file stops
describing reality.
Failure Modes and Fixes
Wrong interpreter. The single most common cause of "I installed it but the import fails". Confirm which Python is running and where it looks.
which python python3 pip # every match on PATH python -c "import sys; print(sys.executable)" python -c "import sys; print(sys.prefix, sys.base_prefix, sep='\n')" # prefix != base_prefix means you ARE in a venv echo "$VIRTUAL_ENV" python -c "import sys; print(*sys.path, sep='\n')" python -c "import requests; print(requests.__file__)" # which copy loaded python -m site # site-packages dirs, in order
error: externally-managed-environment (PEP 668). You are installing into a
Python owned by the OS package manager, which pip now refuses to modify. The fix is
a venv, not the override.
python3 -m venv .venv && source .venv/bin/activate && python -m pip install pkg pipx install some-cli-tool # if it is a CLI tool python -m pip install --break-system-packages pkg # last resort, can break the OS
ModuleNotFoundError right after a successful install.
- The install went to a different interpreter. Run
python -m pip install, not pip install.
- The venv is not active in this shell. Activation is per shell session.
- Your editor or notebook kernel points at another interpreter. In a notebook,
%pip install pkg targets the running kernel.
- The import name differs from the package name: pip install
beautifulsoup4 → import bs4, opencv-python → import cv2,
Pillow → import PIL, PyYAML → import yaml,
python-dateutil → import dateutil, scikit-learn → import sklearn.
- Your own package is not installed. From the project root, pip install -e ..
- A local file shadows the real module. A random.py or json.py next to your
script wins over the standard library. Check pkg.__file__.
Other frequent errors.
| Message | Cause and fix |
|---|---|
No matching distribution found | no wheel for this Python or platform. Check requires-python, try an older release |
Building wheel for X ... error | source build needs a compiler or headers. Prefer a wheel, or install the system dev package |
ResolutionImpossible | conflicting pins. pip install --dry-run to see the conflict, then relax a bound |
command not found after install | the venv's bin is not on PATH, or entry points need a reinstall |
Permission denied on install | you are outside a venv. Do not sudo pip |
| pip and code disagree on a version | stale .pyc or a duplicate install. pip uninstall twice, then reinstall |
When an environment stops making sense, delete and rebuild it. It costs seconds
with uv and it is always faster than debugging.
rm -rf .venv && uv venv && uv pip install -r requirements.txt