Skip to content

Instantly share code, notes, and snippets.

@IceWreck
Last active January 6, 2026 22:01
Show Gist options
  • Select an option

  • Save IceWreck/a44c3294edce034485a5e1454aad86f3 to your computer and use it in GitHub Desktop.

Select an option

Save IceWreck/a44c3294edce034485a5e1454aad86f3 to your computer and use it in GitHub Desktop.
Python New App Boilerplat
# Python Boilerplate
### Tree
This is how the repo should look like.
```
.
├── AGENTS.md
├── config
│ └── example.yml
├── Dockerfile
├── Makefile
├── pyproject.toml
├── README.md
├── requirements.txt
├── src
│ └── appname
│ ├── config.py
│ ├── __init__.py
│ ├── logger.py
│ ├── main.py
│ ├── py.typed
└── uv.lock
```
### Pyproject
In the empty git repo, init with `uv init --app --package ./`.
After that add in the following to pyproject.
```toml
[tool.ruff]
line-length = 120
indent-width = 4
[tool.ruff.lint]
extend-select = [
"F",
"W",
"E",
"I",
"UP",
"C4",
"FA",
"ISC",
"ICN",
"RET",
"SIM",
"TID",
"TC",
"PTH",
"TD",
"NPY",
]
[tool.ruff.format]
indent-style = "space"
quote-style = "double"
[tool.pyright]
typeCheckingMode = "strict"
[dependency-groups]
dev = [
"basedpyright",
"ruff",
]
```
### Agents MD
`AGENTS.md` file also symlink to `CLAUDE.md`.
```markdown
# Agents Knowledge
## Overview
## Code Conventions
- Write modular, clean and fully typed modern python code.
- Use new style Python types (e.g., `list`, `dict`, `tuple`, `set`, `int | None` instead of `List`, `Dict`, `Tuple`, `Set`, `Optional`).
- Use lower case for logs: logger.info("starting xyz").
- Prefer logger instead of print statements.
- Log lines and exceptions should always start with a lowercase char.
- Log lines should not end with a period.
- Try not to use `Any` for typing.
- All code should be typed using modern python.
- Use early returns to reduce indentation.
- Extract complex logic into functions.
- Flatten loops with list comprehensions.
- Leverage data structures instead of deeply nested conditions.
- Write self-explanatory code – Prefer clear variable and function names over comments.
- Explain "why," not "what" – Comments should clarify intent, not restate code.
- Avoid redundant comments – Don't comment obvious things.
- Use comments for complex logic – Explain non-trivial decisions or workarounds.
- Write docstrings for functions/classes – Document purpose, inputs, and outputs.
- Keep comments updated – Outdated comments are worse than none.
- Use inline comments sparingly – Only when necessary for clarity.
- Let Python handle exceptions by default - prefer crashing over complex error handling
- Only add try-except blocks when:
- Explicitly required to keep the application running.
- Requested by the user/product requirements.
- Handling a specific, recoverable error case.
- Instead of defensive programming with try-except blocks:
- Use assertions to validate critical assumptions.
- Let functions fail fast with invalid inputs - Don't catch broad exceptions (except Exception).
- Trust Python's built-in error handling.
- Let stack traces expose issues during development.
## Development
### Typechecking
```bash
make typecheck
```
### Linting
```bash
make lint
```
### Formatting
```bash
make format
```
### Dependency Management
We use uv to manage Python dependencies.
```bash
uv add <your-package-name>
```
This installs the package and adds it to `pyproject.toml`.
```
### Makefile
```
#!make
-include .env
export $(shell sed 's/=.*//' .env)
SHELL := /bin/bash
IMAGE_NAME = example.com/packages/appname:latest
.PHONY: *
develop:
uv venv ./.venv
uv sync --all-extras
touch .env
format:
uv run ruff format ./src
lint:
uv run ruff check --fix src/
uv run ruff check
typecheck:
uv run basedpyright ./src
build:
uv export --format requirements.txt --no-emit-project --no-hashes -o requirements.txt
podman build -t $(IMAGE_NAME) .
```
### Logger
```python
import logging
import os
_logging_configured = False
def setup_logging(force: bool = False) -> None:
global _logging_configured
if not force and _logging_configured:
return
level_name = os.getenv("LOG_LEVEL", "INFO").upper()
level = getattr(logging, level_name, logging.INFO)
# force configure root logger - remove any existing handlers and add ours
root_logger = logging.getLogger()
root_logger.handlers.clear() # remove any existing handlers
root_logger.setLevel(level)
# add our handler with priority
handler = logging.StreamHandler()
handler.setLevel(level)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
root_logger.addHandler(handler)
_logging_configured = True
def get_logger(name: str) -> logging.Logger:
setup_logging()
return logging.getLogger(name)
```
### Config
Use pydantic settings for config management.
```python
class Config(BaseSettings):
model_config = SettingsConfigDict(env_prefix="APPNAME_")
bar: BarConfig = Field(default=...)
foo: FooServerConfig = Field(default_factory=FooServerConfig)
@classmethod
def from_yaml(cls, file_path: Path) -> "Config":
logger.info(f"loading config file: {file_path}")
if not file_path.exists():
logger.error(f"config file not found: {file_path}")
raise FileNotFoundError(f"config file not found: {file_path}")
yaml_source = YamlConfigSettingsSource(cls, yaml_file=str(file_path))
return cls(**yaml_source())
def to_json(self) -> str:
return json.dumps(self.model_dump(mode="json"), indent=4)
```
### Main
Just a hello world using the logger and typer for CLI creation.
It should load config file and then log it.
### Gitignore
```
# Python
__pycache__/
*.py[cod]
*.egg-info/
.venv/
.mypy_cache/
# Environment files
.env
.env.*
# Build
build/
dist/
# IDE
.vscode/
.idea/
# OS
.DS_Store
# Other
/drafts
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment