2.9 KiB
2.9 KiB
name, description
| name | description |
|---|---|
| python-development-layout | Folder structure and file organization for Python services. Use when creating a new service, reorganizing modules, or reviewing project structure. |
Rules
- Every repository must contain a
README.md. - Every repository must contain a
.gitignorethat excludes.venv/,*.pyc, and__pycache__/. - Every repository must contain a
.dockerignorewith content similar to.gitignore. - Every repository must contain a
Dockerfilefor containerized deployment. - Every repository must contain a
pyproject.tomlcreated automatically byuv init. - The Python package source folder lives in the repository root (e.g.
my_package/), not under asrc/directory. pyproject.tomlmust explicitly name the root package folder so the build system knows where the code lives.- All tool configuration (tests, linters, formatters, type checkers, coverage, etc.) must live inside
pyproject.tomlunder the[tool.*]tables. Do not use separate dot-files such as.pylintrc,.flake8,setup.cfg, ortox.inifor tool settings. - The repository root may also contain a
tests/folder and adocs/folder alongside the package folder. - Inside the root package every sub-package is either a service package, an adapter package, or a library package. The structure of each must follow the conventions below.
- The root package must contain a
__main__.pymodule. It is the executable entry point when the package is run withpython -m package_name. Keep it minimal: import only from sibling modules (relative imports), call the factory that builds the CLI or the root service, and invoke it underif __name__ == "__main__":. No business logic lives here. Example:from .cli import get_cli if __name__ == "__main__": cli = get_cli() cli()
Service package layout
A package that exposes a runnable service must contain at minimum:
service.py— the service class. It inherits from the async service mixin and implementsstart/stoplifecycle methods. The constructor accepts exactly one settings instance — the class declared in the same package'ssettings.py. Example:import facet from .settings import MyServiceSettings class MyService(facet.AsyncioServiceMixin): def __init__(self, settings: MyServiceSettings): self._settings = settings async def start(self): # service startup logic passsettings.py— a Pydantic Settings class that declares the configuration schema for the service.interfaces.pyorbase.py— abstract ports (interfaces) that the service depends on. Consumers import from here, never from adapters.fabric.py— a factory module that instantiates the service and its collaborators. It receives settings and returns fully constructed objects.__init__.py— public API of the package. Exports only the types and functions intended for external use.