With pi-pilgrim
DrivineAssembly is a pi-pilgrim assembly holding the registry
and the managers, for your own assembly to use:
from pipilgrim import AssemblyHolder, PilgrimAssemblyfrom drivine import DrivineAssembly # drivine.sync's for the sync API
class AppAssembly(PilgrimAssembly): def __init__(self): self.drivine = AssemblyHolder.shared(DrivineAssembly) super().__init__()
def route_repository(self) -> RouteRepository: return self.shared(lambda: RouteRepository(self.drivine.persistence_manager()))
def report_repository(self) -> ReportRepository: return self.shared(lambda: ReportRepository(self.drivine.persistence_manager("analytics")))Open your assembly with async with, start Drivine in it, and leave it when the application stops. In FastAPI, that
is the lifespan:
from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanagerasync def lifespan(app: FastAPI): async with AppAssembly() as assembly: await assembly.drivine.start() # registers the databases and holds the schema app.state.assembly = assembly yield
app = FastAPI(lifespan=lifespan)- Closing is pi-pilgrim’s. Leaving
async with(orawait assembly.aclose()) closes the assembly’ssharedcomponents, newest first, then theDrivineAssemblyit holds, which closes every database’s driver and pool, as Spring closes drivine4j’s with its context. Your own components close too: whichever ofaclose(),close()orshutdown()each has, ordestroy=onshared. A closed assembly makes no more components. - An assembly closes the ones it asked for first.
AssemblyHolder.shared(DrivineAssembly)in your assembly’s__init__makes your assembly its owner. One that something else asked for first is closed byawait AssemblyHolder.aclose(). - Entering your assembly doesn’t start Drivine, as entering
DrivineAssemblyitself does: callawait assembly.drivine.start(), as above. - The async assembly is an
AsyncPilgrimAssembly, as its drivers close asynchronously:await close(),asyncio.run(close()),await aclose()andasync withall close it, andwithrefuses, and says to useasync with. Awaitclose(): left unawaited, it closes nothing, and Python warns that a coroutine was never awaited. Your assembly closes it whether it holds it withAssemblyHolder.shared(DrivineAssembly)or as a component,self.shared(DrivineAssembly). The sync assembly,drivine.sync.DrivineAssembly, is a plainPilgrimAssembly, which closes withwithorclose(). - It registers the database the environment describes. To register yours in code, subclass it and override
register_databases(registry). Assembledinjects a manager into a class something else makes:pm: PersistenceManager = Assembled(assembly=DrivineAssembly).
The managers are plain classes, so an application with another container, or none, makes them itself, and closes the
registry with async with DatabaseRegistry() as registry:.
Upgrading from 0.2.0
Section titled “Upgrading from 0.2.0”0.3.0 moved DrivineAssembly onto pi-pilgrim 0.3’s lifecycle: the assembly closes when you leave async with, or when
the application assembly that holds it closes, and closing it closes every database’s driver and pool.
- Shutdown. 0.2.0 said to call
await assembly.drivine.close()when the application stops. That closes everything again in every release after 0.3.0, whereDrivineAssemblyis pi-pilgrim’sAsyncPilgrimAssembly, whoseclose()isaclose()(0.3.0 raisedPilgrimError, orTypeErrorwhen Drivine had made no registry). Await it: called withoutawait, it closes nothing, and Python warns that a coroutine was never awaited. The 0.3 way is to leave your assembly’sasync with, as in the lifespan above, or to callawait assembly.aclose(), which closes your components and then Drivine. To close only Drivine,await assembly.drivine.aclose(). with DrivineAssembly()raisesAsyncTeardownRequired, aPilgrimError: useasync with, ordrivine.sync.DrivineAssemblyfor the sync API, which closes withwithorclose().- Sync
close()on your assembly closes Drivine while it has made no registry, whether your assembly holds it withAssemblyHolder.sharedor withself.shared(DrivineAssembly). Once Drivine has made its registry it refuses withAsyncTeardownRequired, before closing anything, since the drivers close asynchronously: close withawait aclose(). It refuses from the start if your subclass ofDrivineAssemblyoverridesaclose(), which a sync close can’t run.

