Module layout and exports
Use this page for file naming, package layout, __init__.py, and export rules.
File naming
- Use
_types.pyfor type definitions. - Use
_agent.pyfor agents. - Use
_controller.pyfor controllers. - Use
_errors.pyfor custom exceptions. - Use
_prompt.pyfor LLM prompts.
Package and module structure
- Organize modules and subpackages so related code stays together.
- If a package is used by multiple others, place it at their least common level.
- Choose between a module and a subpackage based on the size and cohesion of the code. Public surfaces should stay easy to understand and export cleanly.
- Keep canonical protocol vocabularies in code modules, not in hidden sidecar
files, unless there is a real generation or runtime discovery workflow.
Representative payload examples belong in obvious
fixtures/directories.
Example layout:
pkgname/
├── __init__.py
├── _module.py
├── _shared/
├── _internal_subpkg1/
├── _internal_subpkg2/
└── public_subpkg1/
__init__.py usage
- Use
__init__.pyonly to export public entities. - Do not declare classes, functions, or types in
__init__.py. - If
__init__.pyimports helpers for its own logic, delete those helpers at the end of the file.
Example:
import os
from ._events import DetectedEvent, StoredEvent
from ._spike import Spike
__all__ = [
"DetectedEvent",
"StoredEvent",
"Spike",
]
DEVENV = False
if os.environ.get("DEVENV", False) == "True":
DEVENV = True
del os
Exports and internals
- Prefix internal modules and subpackages with
_. - Entities inside internal modules should stay internal unless exported from
the nearest
__init__.py. - Avoid re-exporting entities from public modules in
__init__.py; that makes import paths unclear for consumers. - Export entities from the nearest nesting level only. Deeper packages should re-export their own public surface.
- For bridge and transport packages, expose supported command and event vocabularies from the upstream package that owns the behavior. Downstream apps should import those constants or enums instead of duplicating string literals.
Examples:
# GOOD example of __init__.py
from ._events import DetectedEvent, StoredEvent
from ._spike import Spike
__all__ = [
"DetectedEvent",
"StoredEvent",
"Spike",
]
# BAD example of __init__.py (confusing import paths)
from .events import DetectedEvent, StoredEvent
from .spike import Spike
__all__ = [
"DetectedEvent",
"StoredEvent",
"Spike",
]
# BAD example of __init__.py (deep level re-export)
from ._events._detected import DetectedEvent
from ._events._stored import StoredEvent
__all__ = [
"DetectedEvent",
"StoredEvent",
]