Skip to main content

Python

Modern, pragmatic Python cheat sheet focused on current best practices. Skips basics like variables/operators.

Versions & virtual environments

  • Prefer one Python per project; keep the venv inside the repo (.venv/).
  • Use Poetry for project/dep management; use pipx to install Poetry and other global CLIs.

Setup (zsh):

# Install Poetry as an isolated CLI
pipx install poetry

# Keep virtualenvs in-project (creates .venv/ next to pyproject)
poetry config virtualenvs.in-project true

# Initialize project in an existing folder
poetry init -n # -n = accept defaults, edit pyproject later

# Install deps & create .venv
poetry install --no-root # if no local package yet
# or
poetry install # if local package exists (adds editable install)

Activate/deactivate:

# Preferred: ephemeral shells
poetry shell # spawn a shell within the venv
exit # leave

# Or source the in-project venv directly (because of the config above)
source ./.venv/bin/activate
deactivate

Add .venv/ to .gitignore.

Package management

Common commands:

poetry add httpx pydantic typer              # runtime deps
poetry add --group dev ruff mypy pytest pytest-cov pre-commit
poetry remove pkgname
poetry update # update all
poetry lock # refresh lock
poetry install # sync from lock
poetry run <cmd> # run within venv

Example minimal pyproject.toml (Poetry + PEP 621 metadata):

[tool.poetry]
name = "example"
version = "0.1.0"
description = "Example project"
authors = ["Your Name <you@example.com>"]
readme = "README.md"
packages = [{ include = "example" }]

[tool.poetry.dependencies]
python = ">=3.11,<4.0"
httpx = "*"
pydantic = ">=2"
typer = { version = "*", extras = ["all"] }

[tool.poetry.group.dev.dependencies]
pytest = "*"
pytest-cov = "*"
ruff = "*"
mypy = "*"
pre-commit = "*"

[tool.poetry.scripts]
example = "example.__main__:app" # e.g., Typer app entrypoint

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Run commands through Poetry:

poetry run python -V
poetry run example # from [tool.poetry.scripts]

Alternative A: pip + pip-tools

python -m venv .venv && source .venv/bin/activate
pip install -U pip pip-tools
pip-compile pyproject.toml -o requirements.txt
pip-sync requirements.txt

Alternative B: uv (fast all-in-one)

If you prefer uv:

pipx install uv
uv init && uv add httpx pydantic typer && uv add --dev ruff mypy pytest
uv sync && uv run pytest -q

Code quality & typing

  • Ruff: formatter + linter (fast, single tool)
  • Mypy or Pyright: static typing
  • Pre-commit: enforce on commit

pyproject.toml snippets:

[tool.ruff]
line-length = 100
target-version = "py311"

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM", "PL"]

[tool.mypy]
python_version = "3.11"
disallow_untyped_defs = true
warn_return_any = true
warn_unused_ignores = true
strict_optional = true

Pre-commit minimal config (.pre-commit-config.yaml):

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
hooks:
- id: ruff
args: ["--fix"]
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy

Typical workflow:

poetry add --group dev ruff mypy pre-commit
poetry run pre-commit install
poetry run ruff format .
poetry run ruff check . --fix
poetry run mypy .

Testing

  • Use pytest for simple, readable tests.
  • Keep tests under tests/ mirroring package structure.
poetry add --group dev pytest pytest-cov
poetry run pytest -q --cov

Minimal test example:

def add(a: int, b: int) -> int:
return a + b

def test_add():
assert add(1, 2) == 3

Logging (stdlib)

import logging

logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
logger = logging.getLogger("app")

logger.info("started")

HTTP (httpx)

import httpx

def get_json(url: str) -> dict:
with httpx.Client(timeout=10) as client:
r = client.get(url)
r.raise_for_status()
return r.json()

Async variant:

import asyncio
import httpx

async def fetch(url: str) -> dict:
async with httpx.AsyncClient(timeout=10) as client:
r = await client.get(url)
r.raise_for_status()
return r.json()

async def main():
data = await fetch("https://api.github.com")
print(data["current_user_url"]) # example

if __name__ == "__main__":
asyncio.run(main())

CLI apps (Typer)

import typer

app = typer.Typer()

@app.command()
def greet(name: str, shout: bool = False):
msg = f"Hello, {name}!"
print(msg.upper() if shout else msg)

if __name__ == "__main__":
app()

Settings & validation (Pydantic v2)

from pydantic import BaseModel, ValidationError

class Config(BaseModel):
api_url: str
retries: int = 3

try:
cfg = Config(api_url="https://example.com")
except ValidationError as e:
print(e)

Environment-backed settings:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
api_key: str

class Config:
env_prefix = "APP_"

s = Settings() # reads APP_API_KEY

Filesystem (pathlib)

from pathlib import Path

root = Path(__file__).parent
for p in root.glob("**/*.py"):
print(p)

Subprocess

import subprocess

result = subprocess.run(
["git", "status", "--porcelain"],
check=True,
capture_output=True,
text=True,
)
print(result.stdout)

Async concurrency

import asyncio

async def work(i: int) -> int:
await asyncio.sleep(0.1)
return i * 2

async def main():
results = await asyncio.gather(*(work(i) for i in range(5)))
print(results)

if __name__ == "__main__":
asyncio.run(main())

Packaging (with Poetry)

  • Metadata lives in pyproject.toml under [tool.poetry].
  • Build wheels/sdist with poetry build.
  • Publish with poetry publish (set token or pass creds).
# Configure token once (writes to Poetry config)
poetry config pypi-token.pypi <pypi_token>

# Or publish explicitly with token
poetry publish --build --username __token__ --password <pypi_token>

One-file scripts with inline deps (PEP 723, optional)

Poetry doesn’t run PEP 723 inline-deps directly; use uv if you like this style:

# /// script
# requires-python = ">=3.11"
# dependencies = ["rich"]
# ///

from rich import print

print("[bold green]Hello[/bold green]")

Run with uv (installs deps in an ephemeral env):

uv run script.py