|
| 1 | +# **Lifespan** |
| 2 | +Ellar applications registers a lifespan manager. The manager handles lifespan handler registered in the configuration under the variable name |
| 3 | +**`DEFAULT_LIFESPAN_HANDLER`**. |
| 4 | + |
| 5 | +It also executes code that needs to run before the application starts up, or when the application is shutting down. |
| 6 | +The lifespan manager must be run before ellar starts serving incoming request. |
| 7 | + |
| 8 | +```python |
| 9 | +import uvicorn |
| 10 | +import contextlib |
| 11 | +from ellar.core import App, AppFactory |
| 12 | + |
| 13 | +@contextlib.asynccontextmanager |
| 14 | +async def some_async_resource(): |
| 15 | + print("running some-async-resource function") |
| 16 | + yield |
| 17 | + print("existing some-async-resource function") |
| 18 | + |
| 19 | + |
| 20 | +@contextlib.asynccontextmanager |
| 21 | +async def lifespan(app: App): |
| 22 | + async with some_async_resource(): |
| 23 | + print("Run at startup!") |
| 24 | + yield |
| 25 | + print("Run on shutdown!") |
| 26 | + |
| 27 | + |
| 28 | +application = AppFactory.create_app(config_module=dict( |
| 29 | + DEFAULT_LIFESPAN_HANDLER=lifespan |
| 30 | +)) |
| 31 | + |
| 32 | +if __name__ == "__main__": |
| 33 | + uvicorn.run(application, port=5000, log_level="info") |
| 34 | +``` |
| 35 | +The construct above will generate the output below: |
| 36 | +```shell |
| 37 | +INFO: Started server process [11772] |
| 38 | +INFO: Waiting for application startup. |
| 39 | +INFO: Application startup complete. |
| 40 | +INFO: Uvicorn running on http://127.0.0.1:5000 (Press CTRL+C to quit) |
| 41 | + |
| 42 | +running some-async-resource function |
| 43 | +Run at startup! |
| 44 | + |
| 45 | +INFO: Shutting down |
| 46 | +INFO: Waiting for application shutdown. |
| 47 | +INFO: Application shutdown complete. |
| 48 | +INFO: Finished server process [11772] |
| 49 | + |
| 50 | +Run on shutdown! |
| 51 | +existing some-async-resource function |
| 52 | +``` |
| 53 | + |
| 54 | +## **Modules and Lifespan** |
| 55 | +Any module that wants to engage in application lifespan must inherit `IApplicationStartup` for startup actions or `IApplicationShutdown` for shutdown actions |
| 56 | +or inherit both for startup and shutdown actions. |
| 57 | + |
| 58 | +`IApplicationStartup` has an abstractmethod `on_startup` function and `IApplicationShutdown` has an abstractmethod `on_shutdown` function. |
| 59 | + |
| 60 | +```python |
| 61 | +from abc import abstractmethod |
| 62 | + |
| 63 | + |
| 64 | +class IApplicationStartup: |
| 65 | + @abstractmethod |
| 66 | + async def on_startup(self, app: "App") -> None: |
| 67 | + ... |
| 68 | + |
| 69 | + |
| 70 | +class IApplicationShutdown: |
| 71 | + @abstractmethod |
| 72 | + async def on_shutdown(self) -> None: |
| 73 | + ... |
| 74 | +``` |
| 75 | +Let's assume we have a module that extends both `IApplicationStartup` and `IApplicationShutdown` to execute some actions on startup and on shutdown as shown below: |
| 76 | + |
| 77 | +```python |
| 78 | +from ellar.common import IApplicationShutdown, IApplicationStartup, Module |
| 79 | + |
| 80 | +@Module() |
| 81 | +class SampleModule(IApplicationShutdown, IApplicationStartup): |
| 82 | + |
| 83 | + async def on_startup(self, app) -> None: |
| 84 | + print("Run at startup! in SampleModule") |
| 85 | + |
| 86 | + async def on_shutdown(self) -> None: |
| 87 | + print("Run on shutdown! in SampleModule") |
| 88 | + |
| 89 | +``` |
| 90 | + |
| 91 | +## **Running lifespan in tests** |
| 92 | +You should use `TestClient` as a context manager, to ensure that the lifespan is called. |
| 93 | + |
| 94 | +```python |
| 95 | +from ellar.testing import Test |
| 96 | +from .main import SampleModule |
| 97 | + |
| 98 | +test_module = Test.create_test_module(modules=[SampleModule]) |
| 99 | + |
| 100 | +def test_lifespan(): |
| 101 | + with test_module.get_test_client() as client: |
| 102 | + # Application's lifespan is called on entering the block. |
| 103 | + response = client.get("/") |
| 104 | + assert response.status_code == 200 |
| 105 | + |
| 106 | + # And the lifespan's teardown is run when exiting the block. |
| 107 | + |
| 108 | +``` |
0 commit comments