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
ShellActivateDeactivate
bash · zsh (macOS, Linux)source .venv/bin/activatedeactivate
fishsource .venv/bin/activate.fishdeactivate
PowerShell.venv\Scripts\Activate.ps1deactivate
cmd.exe.venv\Scripts\activate.batdeactivate
.venv/bin/python -m pip install requests   # no activation needed, always works

Activation 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 .gitignore and commit the dependency file instead. Name it .venv (the convention every editor and uv auto-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.

CommandEffect
python -m pip install requestslatest 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.txteverything in a file
python -m pip install -e .editable install of the current project
python -m pip install -U requestsupgrade (--upgrade)
python -m pip install --no-deps pkgskip dependencies, for manual pinning
python -m pip install --dry-run pkgresolve and report, install nothing
python -m pip install "uvicorn[standard]"with an extras group
python -m pip install --index-url URL pkgreplace PyPI entirely
python -m pip install --extra-index-url URL pkgadd a second index
python -m pip install --only-binary :all: pkgrefuse source builds
python -m pip install --no-cache-dir pkgignore the wheel cache
python -m pip install --user pkginto 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

ToolScopeLock fileNotes
venv + pipenv + installno (use pip-tools)stdlib, zero install, the baseline
uvenv, install, lock, run, Python versionsuv.lockRust, 10-100× faster, drop-in pip interface
pipxinstall CLI appsnoone isolated env per tool, not for project deps
poetryenv, install, lock, build, publishpoetry.lockopinionated, own CLI, its own resolver
pdmenv, install, lock, buildpdm.lockPEP 621 native, supports PEP 582 local packages
conda · mambaenv, install, non-Python depsenvironment.ymlships CUDA, MKL, compilers. Scientific stacks
pyenvinterpreter versions onlynopairs with any of the above
hatchenv matrix, build, publishnobuild 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.

MessageCause and fix
No matching distribution foundno wheel for this Python or platform. Check requires-python, try an older release
Building wheel for X ... errorsource build needs a compiler or headers. Prefer a wheel, or install the system dev package
ResolutionImpossibleconflicting pins. pip install --dry-run to see the conflict, then relax a bound
command not found after installthe venv's bin is not on PATH, or entry points need a reinstall
Permission denied on installyou are outside a venv. Do not sudo pip
pip and code disagree on a versionstale .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