Common Workflows
This page collects practical workflows for building an application with Equipment after the project has been generated.
Create A New Application Service
- Add a service class under
app/. - Register it in
app/__init__.pyif it needs framework services or shared lifecycle management. - Add tests under
tests/. - Call the service from
main.py,web.py, a queue job, or a scheduled task.
Example service:
# app/Reports.py
from equipment.Storage.AbstractStorage import AbstractStorage
from equipment.Log.AbstractLogger import AbstractLogger
class Reports:
def __init__(self, storage: AbstractStorage, log: AbstractLogger):
self.storage = storage
self.log = log
def write_daily_report(self, content: str) -> str:
path = "reports/daily.txt"
self.storage.write(path, content)
self.log.info("Daily report written", extra={"path": path})
return path
Register it:
# app/__init__.py
from dependency_injector.providers import ThreadSafeSingleton as Singleton
from equipment import Equipment
from app.Reports import Reports
class App(Equipment):
reports = Singleton(Reports, Equipment.storage, Equipment.log)
Use it:
from app import app
application = app()
application.reports().write_daily_report("Ready")
Add Configuration For A Service
Create config/reports.yaml:
reports:
output_path: ${REPORTS_OUTPUT_PATH:reports/daily.txt}
enabled: ${REPORTS_ENABLED:true}
Inject the config value:
class App(Equipment):
reports = Singleton(
Reports,
Equipment.storage,
Equipment.log,
Equipment.config.reports.output_path,
)
Use environment variables in .env for machine-specific values:
REPORTS_OUTPUT_PATH=reports/local-daily.txt
Add A Database-backed Feature
- Define SQLAlchemy models in your application code.
- Add or update Alembic migration metadata.
- Create a migration under
database/migrations. - Run the migration locally.
- Add repository/service tests.
Typical commands:
cd database/migrations
alembic revision --autogenerate -m "create invoices table"
alembic upgrade head
Use sessions carefully:
session = application.database().session()
try:
session.add(invoice)
session.commit()
except Exception:
session.rollback()
raise
finally:
session.close()
Add A Queued Task
Put queued functions at module scope so Redis/RQ can import them:
# app/jobs.py
from app import app
def rebuild_report(report_id: int) -> None:
application = app()
application.log().info("Rebuilding report", extra={"report_id": report_id})
Queue it from a script or route:
from app import app
from app.jobs import rebuild_report
application = app()
application.queue().push(rebuild_report, 123)
Local development can keep QUEUE_CONNECTION=sync. Production worker mode usually sets QUEUE_CONNECTION=redis and runs:
python queues.py
Add A Scheduled Task
Add scheduled work in app/Scheduler.py:
from app.jobs import rebuild_report
class Scheduler(Equipment):
def run(self) -> None:
self.schedule.every().day.at("03:00").do(
self.queue.push,
rebuild_report,
123,
)
super().run()
Run it with:
python scheduler.py
Use the queue for slow work so the scheduler loop stays responsive.
Add A FastAPI Route
Add routes in web.py for small apps, or move them into a router module for larger apps.
from fastapi import APIRouter
from app import app
router = APIRouter()
application = app()
@router.get("/health")
def health() -> dict[str, str]:
return {"status": "ok", "app": application.config.app.name()}
Then include the router from web.py:
from app.routes import router
web.include_router(router)
Keep route functions thin. Put business logic in services registered on the App container.
Add Tests For A Workflow
Use the generated TestCase when a test needs an application container:
from tests.TestCase import TestCase
class ReportsTest(TestCase):
def test_report_is_written_to_storage(self):
path = self.app.reports().write_daily_report("Ready")
self.assertTrue(self.app.storage().exists(path))
self.assertEqual("Ready", self.app.storage().read(path))
Use unittest.mock for external systems that should not run in unit tests:
from unittest.mock import Mock
self.app.storage.override(Mock())
Prepare For Deployment
Before deployment, decide these values explicitly:
APP_ENV: environment name such asproduction,staging, orlocal.LOG_CHANNEL:console,stack,single,daily,sqlite, ornull.DB_CONNECTION:sqlite,mysql, orpostgresql.QUEUE_CONNECTION:syncorredis.FILESYSTEM_DISK:localors3.WEB_HOSTandPORT: web server binding values.
Run a deployment-style smoke test:
python -m pip install .
python -m unittest discover -s tests
python main.py
If you compile the project, smoke test the compiled output too:
equipment compile dist
cd dist
python main.pyc
Upgrade Dependencies Safely
Dependency upgrades should be their own change. Before upgrading, run the current tests to establish a baseline. After upgrading, run:
python -m coverage run -m unittest discover -s tests
python -m coverage report
cd project
python -m pip install .
python -m unittest discover -s tests
Pay close attention to SQLAlchemy, boto3/botocore/moto, redis/rq, python-json-logger, dependency-injector, and FastAPI/Uvicorn upgrades because Equipment integrates directly with those packages.