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
Poetry (recommended)
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.tomlunder[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
Handy links
- Poetry: https://python-poetry.org/docs/
- Packaging: https://packaging.python.org/
- Ruff: https://docs.astral.sh/ruff/
- Pytest: https://docs.pytest.org/
- Pydantic: https://docs.pydantic.dev/
- httpx: https://www.python-httpx.org/