Injector Context¶
In the complex web of dependencies within Ellar, accessing the application instance without triggering circular import errors can be a daunting task.
To address this challenge, Ellar introduces the concept of an injector context, a powerful mechanism that facilitates smooth access to the application instance throughout various parts of the codebase.
Understanding the Injector Context¶
The Injector context serves as a bridge between different components of the application, making the application object readily available through the application Dependency Injection (DI) container. This ensures that you can access the application instance without encountering import issues, whether it's during request handling, execution of CLI commands, or manual invocation of the context.
Two key elements of the application context are:
- current_injector: This proxy variable refers to the current application injector, providing access to any service or the application instance itself.
- current_config: A lazy loader for application configuration, which is based on the
ELLAR_CONFIG_MODULE
reference or accessed throughapplication.config
when the application context is active.
Integration with Click Commands¶
By decorating Click commands with click.with_app_context
, you can effortlessly incorporate the application context into their CLI workflows. This allows them to leverage the full power of the application context when executing commands.
For instance, consider the following example:
import ellar_cli.click as click
from ellar.core import current_injector
from ellar.di import injectable
@injectable
class MyService:
def do_something(self) -> None:
pass
@click.command()
@click.argument('name')
@click.with_injector_context
def my_command(name: str):
my_service = current_injector.get(MyService)
my_service.do_something()
Here, the click.with_app_context
decorator ensures that the application context is available within the my_command
function, allowing seamless access to application resources.
Lifecycle of the Application Context¶
The application context operates as an asynchronous context manager, automatically created at the beginning of request handling and gracefully torn down upon completion of the request.
Handling Application Context Events¶
Additionally, the application context offers event hooks that you can leverage to perform custom actions at specific points in the context's lifecycle.
Two such events are app_context_started
and app_context_teardown
, which are triggered at the beginning and end of the context, respectively.
For example, consider the following event handling setup:
from ellar.events import app_context_teardown, app_context_started
from ellar.app import App, AppFactory
from ellar.core import current_injector, injector_context
from ellar.threading import run_as_sync
@app_context_started.connect
async def on_context_started():
print('=======> Context Started <=======')
@app_context_teardown.connect
def on_context_ended():
print('=======> Context ended <=========')
app = AppFactory.create_app()
@run_as_sync
async def run_app_context():
async with injector_context(app.injector) as ctx:
print("-----> Context is now available <------")
app_instance = ctx.injector.get(App)
app_instance_2 = current_injector.get(App)
assert app_instance == app == app_instance_2
print("-----> Context is now destroyed <------")
if __name__ == '__main__':
run_app_context()
In this example, event handlers are registered to execute custom logic when the application context starts and ends. This allows you to perform initialization or cleanup tasks as needed.