Guidelines for AI agents working in this repository.
| Task | Command |
|---|---|
| Install deps | uv sync --extra test --extra dev --extra linter |
| Run application | uv run python -m python_boilerplate |
| Lint (ruff) | uv run ruff check . |
| Lint auto-fix | uv run ruff check --fix . |
| Format | uv run ruff format . |
| Type check | uv run mypy |
| Install mypy stubs | uv run mypy --install-types |
| All tests | uv run pytest --cov -n auto --benchmark-disable |
| Single test file | uv run pytest tests/test_python_boilerplate/common/test_profiling.py |
| Single test func | uv run pytest tests/test_python_boilerplate/common/test_profiling.py -k test_elapsed_time |
| Tests with logging | uv run pytest --log-cli-level=DEBUG --capture=no <path> |
| Benchmarks only | uv run pytest --capture=no --log-cli-level=ERROR -n 0 --benchmark-only |
| Pre-commit (all) | uv run pre-commit run --all-files |
src/
python_boilerplate/
__init__.py # freeze_support() call for PyInstaller
__main__.py # Application entry point
common/ # Shared utilities (decorators, helpers)
configuration/ # App config, thread pool, logging, scheduler, DB
data_migration/ # Database migration scripts
demo/ # Example/demo scripts
message/ # Messaging (email)
repository/ # Data access layer (peewee ORM models + repos)
resources/ # Static resources (HOCON config files)
template/ # Jinja2 templates
tests/
conftest.py # Global fixtures (auto_profile with pyinstrument)
test_python_boilerplate/ # Mirrors src/ structure exactly
common/
configuration/
demo/
message/
repository/
template/
Source lives in src/ (setuptools package-dir = {"" = "src"}). Tests mirror the source tree under tests/test_python_boilerplate/.
- Python: >=3.13.1, <3.14 (
target-version = "py313") - Package manager: uv (with
uv.lock) - Linter/Formatter: Ruff 0.15.x (
lint.select = ["ALL"]with specific ignores) - Type checker: mypy (strict mode, pydantic plugin)
- Test runner: pytest with pytest-xdist (
-n auto), pytest-cov, pytest-mock, pytest-asyncio, pytest-benchmark - Pre-commit hooks: ruff import sort, ruff check --fix, ruff format, mypy, pytest (pre-commit stage), pytest-cov (pre-push stage)
- Line length: 120 characters
- Indent: 4 spaces, UTF-8, LF line endings
Imports are organized in three groups separated by blank lines, sorted by ruff (I001):
# 1. Standard library
import functools
import time
from collections.abc import Callable
from typing import Any
# 2. Third-party
from loguru import logger
# 3. Local (absolute imports only, no relative imports)
from python_boilerplate.common.common_function import json_serial- Use
from collections.abc import Callable, Coroutine(NOTfrom typing import Callable) - Relative imports are banned (
ban-relative-imports = "all"in ruff config) __init__.pyfiles are mostly empty (no re-exports)
- All functions MUST have return type annotations (mypy
disallow_untyped_defs = true) - Use PEP 695 type parameter syntax for generic functions:
def foo[T](x: T) -> T:(NOTTypeVar) - Use
typing.Anywhen dynamic types are unavoidable - Use
typing.Finalfor module-level constants - Use
TYPE_CHECKINGguard for import-only types:if TYPE_CHECKING: from _pytest.nodes import Node - Use
X | Yunion syntax (NOTUnion[X, Y]orOptional[X])
- Files:
snake_case.py - Functions/variables:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASEwithFinalannotation - Test files:
test_<module_name>.pymirroring source structure - Test functions:
test_<what_is_tested>ortest_<what_is_tested>_<scenario>
Sphinx/reST style with :param: and :return: directives:
def get_login_user() -> str:
"""
Get current login user who is using the OS.
:param username: the username to look up
:return: the username
"""- Opening
"""on same line as summary for single-line docstrings - Opening
"""on first line with summary for multi-line docstrings - One blank line between summary and
:paramblock (when description exists)
- Catch specific exceptions, log with
logger.exception()orlogger.error() - Re-raise after logging:
raise e(do not swallow) - Use
pytest.raisesfor testing expected exceptions - Pattern: try/execute/except-log-raise/finally-cleanup
try:
return func(*arg, **kwarg)
except Exception as e:
logger.warning(f"Captured exception. {e}")
raise e
finally:
cleanup()- Use
loguru.loggerexclusively (NOT stdliblogging) - f-string formatting in log messages:
logger.info(f"Result: {value}") - Available levels: TRACE, DEBUG, INFO, WARNING, ERROR
This codebase makes heavy use of function decorators in src/python_boilerplate/common/:
@async_function- Run sync function in thread pool, returnsFuture@async_function_wrapper- Add done_callback to async function@elapsed_time(level="INFO")- Profile function timing@async_elapsed_time(level="INFO")- Profile async function timing@mem_profile()/@cpu_profile()- Resource profiling@peewee_table- Register ORM table class@trace/@async_trace- Function call tracing to DB@debounce(wait)/@throttle(limit)- Rate limiting
- Test files mirror source:
src/.../foo.py->tests/test_python_boilerplate/.../test_foo.py - Use
pytest.fixturefor setup;conftest.pyhasauto_profilefixture (runs pyinstrument on every test) - Use
pytest-mock(MockerFixture) for mocking/spying - Async tests use
@pytest.mark.asyncio - Name pattern:
--pytest-test-firstenforced (functions must start withtest_) - Coverage target: 90% (enforced in pre-push hook)
Key intentionally-ignored lint rules (do NOT "fix" these patterns):
ANN401-typing.Anyis allowed in*args/**kwargsD100-D104,D106- Missing docstrings in public modules/classes/functions are allowedEM101,EM102- String literals in exceptions are OKPLR2004- Magic values in comparisons are OKCOM812- Trailing commas are not enforcedS311- Pseudo-random generators are OKTRY003,TRY201- Exception message style is relaxed
Test files (tests/*) additionally ignore: ANN (annotations), ARG (unused args), INP001, S101 (assert).
GitHub Actions (python-ci-with-uv.yml) runs on push to main/develop/feature/release/hotfix branches:
uv run ruff check .(no--fixin CI - must pass clean)uv run mypy(after installing types)uv run pytest --quiet --cov --cov-report term -n auto --benchmark-disableuv run pytest --benchmark-only(benchmarks separate)- Application smoke test
Note: CI runs ruff check WITHOUT --fix. Code must pass lint cleanly before push.