### Install Project Dependencies Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Use `make install` to set up the pipenv virtual environment with development dependencies. If this fails, run `make setup_pipenv` first to ensure pipenv is up-to-date. ```bash make install # set up pipenv virtualenv with dev dependencies make setup_pipenv # install / upgrade pipenv itself — run this first if `make install` fails ``` -------------------------------- ### Install Lagom Source: https://github.com/meadsteve/lagom/blob/master/README.md Install Lagom using pip, pipenv, or poetry. If cloning from source, ensure you use a version tag. ```bash pip install lagom ``` ```bash # or: # pipenv install lagom # poetry add lagom ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/meadsteve/lagom/blob/master/docs/CONTRIBUTING.md Run this command to set up the pipenv virtual environment for local development. ```bash make install ``` -------------------------------- ### Lagom Container Setup and Object Building Source: https://github.com/meadsteve/lagom/blob/master/docs/full_example.md Sets up a Lagom container, binds dependencies like API URLs and client implementations, and then builds a 'Game' object using the configured container. This is useful for initializing your application's core components. ```python from abc import ABC from lagom import Container #-------------------------------------------------------------- # Here is an example of some classes your application may be built from DiceApiUrl = NewType("DiceApiUrl", str) class RateLimitingConfig: pass class DiceClient(ABC): pass class HttpDiceClient(DiceClient): def __init__(self, url: DiceApiUrl, limiting: RateLimitingConfig): pass class Game: def __init__(self, dice_roller: DiceClient): pass #-------------------------------------------------------------- # Next we setup some definitions container = Container() # We need a specific url container[DiceApiUrl] = DiceApiUrl("https://roll.diceapi.com") # Wherever our code wants a DiceClient we get the http one container[DiceClient] = HttpDiceClient #-------------------------------------------------------------- # Now the container can build the game object game = container[Game] ``` -------------------------------- ### Click Command with Lagom Injection Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Integrate Lagom's dependency injection with Click command-line applications. This example shows how to inject a service into a Click command. ```python container = Container() cli = ClickIntegration(container) @cli.command() @cli.argument("name") def call_the_thing(input, service: SomeServiceClass = injectable): service.do_thing(input) ``` -------------------------------- ### Django Model Injection Setup Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Configure the dependency injection container to make Django models available. List all models that should be injectable. ```python # dependency_config.py # a per app dep injection container from .models import Question container = Container() dependencies = DjangoIntegration(container, models=[Question]) ``` -------------------------------- ### Django Dependency Injection Container Setup Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Set up a dependency injection container for a Django app. This involves creating a `dependency_config.py` file to define services and integrate them with Django. ```python # dependency_config.py # a per app dep injection container container = Container() container[SomeService] = SomeService("connection details etc") dependencies = DjangoIntegration(container) ``` -------------------------------- ### POST /save_it/ Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md An example of a Flask route integrated with Lagom's dependency injection for saving data. ```APIDOC ## POST /save_it/ ### Description This endpoint saves a given item to the database using dependency injection for the Database service. ### Method POST ### Endpoint /save_it/ ### Parameters #### Path Parameters - **thing_to_save** (string) - Required - The item to be saved. #### Request Body This endpoint does not explicitly define a request body in the provided example. ### Request Example ```python # Example of how this route might be called (client-side): # POST /save_it/my_item ``` ### Response #### Success Response (200) - **response** (string) - Indicates that the item was saved. #### Response Example ``` saved ``` ``` -------------------------------- ### Partially Bind Function to Container Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Use the `magic_bind_to_container` decorator to wrap a function and automatically inject arguments from the container. The database in this example will be built by Lagom. ```python from lagom import magic_bind_to_container @magic_bind_to_container(container) def handle_some_request(request, database: DB): # Do something in the database with this request pass ``` -------------------------------- ### Format Code Source: https://github.com/meadsteve/lagom/blob/master/docs/CONTRIBUTING.md Execute this command to format the code according to project standards. ```bash make format ``` -------------------------------- ### Basic Container Usage Source: https://github.com/meadsteve/lagom/blob/master/README.md Instantiate a container and retrieve an object by its type. ```python container = Container() some_thing = container[SomeClass] ``` -------------------------------- ### Run Full Project Verification Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Execute `make test` for comprehensive verification, including mypy, unit tests, doctests, formatting checks, and documentation coverage. This is the default verification target. ```bash make test # full verification: mypy, unit tests, doctests, format, doc coverage ``` -------------------------------- ### Run Benchmark Tests Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Execute benchmark tests using `make benchmark`. This target is used for performance testing and validation. ```bash make benchmark # benchmark tests ``` -------------------------------- ### Run Tests Source: https://github.com/meadsteve/lagom/blob/master/docs/CONTRIBUTING.md Use this command to ensure the build will pass by running all tests. ```bash make test ``` -------------------------------- ### Test a Service Class Directly Source: https://github.com/meadsteve/lagom/blob/master/docs/testing_with_lagom.md Test your service by instantiating it with a mock dependency. This approach avoids global state, patching, and Lagom itself. ```python def test_user_can_be_made_premium(): mock_db = MockDBOfSomeKind() service = UserPremiumService(mock_db) test_user = SomeUser() service.make_user_premium(test_user) assert_mock_db_has_premium_user(mock_db, test_user) ``` -------------------------------- ### Auto-wiring with Type Hints Source: https://github.com/meadsteve/lagom/blob/master/README.md Lagom uses type hints in `__init__` to automatically inject dependencies. No special configuration is needed for simple cases. ```python class MyDataSource: pass class SomeClass: # 👇 type hint is used by lagom def __init__(datasource: MyDataSource): pass container = Container() some_thing = container[SomeClass] # An instance of SomeClass will be built with an instance of MyDataSource provided ``` ```python class SomeClass: # 👇 This is the change. def __init__(datasource: MyDataSource, service: SomeFeatureProvider): pass # Note the following code is unchanged container = Container() some_thing = container[SomeClass] # An instance of SomeClass will be built with an instance of MyDataSource provided ``` -------------------------------- ### Auto-wiring with Type Hints Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Lagom uses type hints in `__init__` to automatically inject dependencies. No special configuration is needed for simple cases. ```python class MyDataSource: pass class SomeClass: # 👇 type hint is used by lagom def __init__(datasource: MyDataSource): pass container = Container() some_thing = container[SomeClass] # An instance of SomeClass will be built with an instance of MyDataSource provided ``` ```python class SomeClass: # 👇 This is the change. def __init__(datasource: MyDataSource, service: SomeFeatureProvider): pass # Note the following code is unchanged container = Container() some_thing = container[SomeClass] # An instance of SomeClass will be built with an instance of MyDataSource provided ``` -------------------------------- ### Create a Test Container Fixture Source: https://github.com/meadsteve/lagom/blob/master/docs/testing_with_lagom.md Clone a production container and replace specific dependencies with stubs or mocks for testing. This allows for controlled testing of components. ```python def container_fixture(): from my_app.prod_container import container test_container = container.clone() # Cloning enables overwriting deps test_container[DiceClient] = StubbedResponseClient() return test_container ``` -------------------------------- ### Define Multiple Database Instances Source: https://github.com/meadsteve/lagom/blob/master/docs/next_steps.md Use explicit definitions to map different connection strings to the same class when multiple instances are needed. ```python class Database: pass class QueryService: def __init__(self, db: Database): pass class DataWriter: def __init__(self, db: Database): pass ``` ```python from lagom import Container container = Container() container[QueryService] = lambda: QueryService(Database("read-replica-connection-string")) container[DataWriter] = lambda: DataWriter(Database("primary-connection-string")) ``` -------------------------------- ### Explicit Build Instructions Source: https://github.com/meadsteve/lagom/blob/master/README.md Provide a function to explicitly define how a class should be constructed. ```python container[SomeClass] = lambda: SomeClass("down", "spiral") ``` -------------------------------- ### Singleton Registration Source: https://github.com/meadsteve/lagom/blob/master/README.md Register a class as a singleton with the container. The instance will be created only once. ```python container[SomeExpensiveToCreateClass] = SomeExpensiveToCreateClass("up", "left") ``` -------------------------------- ### Benchmarking Workflow with Pytest Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Save a baseline benchmark on master and compare against a feature branch using `pytest-benchmark`. Use `--benchmark-save` to record a baseline and `--benchmark-compare` to compare against it. The `make benchmark` target includes `--benchmark-compare-fail=mean:50%` for CI-style regression checks. ```bash git checkout master && pipenv run pytest tests -m "benchmarking" --benchmark-save=master git checkout && pipenv run pytest tests -m "benchmarking" --benchmark-compare=0001 ``` -------------------------------- ### Integrate with OpenTelemetry Source: https://github.com/meadsteve/lagom/blob/master/docs/cookbook.md Integrate Lagom with OpenTelemetry for distributed tracing. Use `dependency_definition` for singletons like `TracerProvider` and `magic_bind_to_container` for request-scoped tracers. ```python from lagom import Container, magic_bind_to_container, dependency_definition container = Container() # The tracer provider is a singleton because we only need one of it @dependency_definition(container, singleton=True) def _get_provider() -> TracerProvider: provider = TracerProvider() processor = SimpleSpanProcessor(ConsoleSpanExporter()) provider.add_span_processor(processor) return provider # A new tracer is built each time it's needed. Later in the code # this will be made a request level singleton so that all actions # in one request share a tracer context container[Tracer] = lambda c: trace.get_tracer(__name__, tracer_provider=c[TracerProvider]) # Note: This class now has no tracer global state # we can easily do some testing around the tracing. class SomeService: def __init__(self, tracer: Tracer): self.tracer = tracer def do_work(self): with self.tracer.start_as_current_span("Doing some work with an API"): return "important data" # When binding this request handling function to the container # we make instances of Tracer shared. This means anything that # asks for a tracer will get the same one. So SomeService and this # function work on the same trace context @magic_bind_to_container(container, shared=[Tracer]) def handle_request(request, tracer: Tracer, service: SomeService): with tracer.start_as_current_span("starting up"): service_response = service.do_work() print(f"ok: {service_response}") ``` -------------------------------- ### Create Test Environment Variables Source: https://github.com/meadsteve/lagom/blob/master/docs/next_steps.md Instantiate Pydantic environment classes with explicit values for testing purposes. ```python test_db_env = DBEnv(db_host="fake", db_password="skip") ``` -------------------------------- ### Enable Reflection Logging for Undefined Dependencies Source: https://github.com/meadsteve/lagom/blob/master/docs/explicit_definitions.md Set `log_undefined_deps=True` when creating a `Container` to log warnings for dependencies that Lagom needs to resolve using reflection. This helps identify which dependencies require explicit definitions. ```python from lagom import Container # You can also pass an existing logger instead of setting True container = Container(log_undefined_deps=True) ``` -------------------------------- ### Define a Singleton Dependency Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Configure a dependency to be instantiated only once. This can be done by directly assigning an instance or by using the `Singleton` wrapper for deferred construction. ```python container[SomeClassToLoadOnce] = SomeClassToLoadOnce("up", "left") ``` ```python container[SomeClassToLoadOnce] = Singleton(SomeClassToLoadOnce) ``` -------------------------------- ### Django URL Configuration Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Standard Django URL configuration to map URLs to the dependency-injected views. ```python from django.urls import path from . import views urlpatterns = [ path('function_view', views.index, name='func_view'), path('class_based', views.CBVexample.as_view(), name='cbv'), ] ``` -------------------------------- ### Define a Service Class for Testing Source: https://github.com/meadsteve/lagom/blob/master/docs/testing_with_lagom.md Define your service class without modifications. Lagom injects dependencies like 'db' at runtime, but the class itself remains unchanged for testing. ```python class UserPremiumService: # The DB here will be injected by lagom at runtime. But we don't need to modify this # class at at all to allow this. def __init__(self, db: DB): pass def upgrade_user_to_premium(self, user): pass ``` -------------------------------- ### Use Lagom with FastAPI Dependency Injection Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Integrate Lagom with FastAPI by using the `deps.depends()` method to provide dependencies in a format FastAPI expects. This allows Lagom's container to manage dependencies for FastAPI routes. ```python container = Container() container[DBConnection] = DB("DSN_CONNECTION_GOES_HERE") app = FastAPI() deps = FastApiIntegration(container) @app.get("/") async def homepage(request, db = deps.depends(DBConnection)): # 👆 This is a lagom method that works like FastAPI's user = db.fetch_data_for_user(request.user) return PlainTextResponse(f"Hello {user.name}") ``` -------------------------------- ### Format Code Automatically Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Apply automatic code formatting using `black` with the `make format` target. This ensures consistent code style across the project. ```bash make format # auto-format with black ``` -------------------------------- ### Click Command with ClickIO Injection Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Inject `ClickIO` into a Click command to handle input/output operations. This provides a standardized way to interact with the command line, useful for testing. ```python @cli.command() @cli.argument("name") def hello(name, io: ClickIO = injectable): io.echo(f"Hello {name}") ``` -------------------------------- ### Run Specific Test Suites Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Use specific `make test_*` targets for focused verification. `make test_unit` runs only unit tests, `make test_mypy` for type checking, `make test_doctests` for embedded doctests, and `make test_format` for code formatting checks. ```bash make test_unit # unit tests only (excluding benchmarks) make test_mypy # mypy type checking make test_doctests # doctests embedded in source modules make test_format # check formatting with black ``` -------------------------------- ### Testing Click Commands with Plain Function Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Test Click commands by accessing their `plain_function` attribute, which retains a reference to the original function. This allows for mocking dependencies without monkey patching. ```python def test_something(): mock_service = mock.create_autospec(SomeServiceClass) call_the_thing.plain_function("something", mock_io) mock_service.do_thing.assert_called_once_with("something") ``` -------------------------------- ### Generate HTML Coverage Report Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md Generate an HTML report for code coverage using the `make coverage` target. This helps in identifying areas of the codebase that are not adequately tested. ```bash make coverage # HTML coverage report ``` -------------------------------- ### Load Classes Based on Environment Variable Source: https://github.com/meadsteve/lagom/blob/master/docs/cookbook.md Use Lagom's `Env` class to map environment variables and conditionally load different class implementations. Ensure environment variables are correctly set or marked as optional. ```python from abc import ABC from lagom import Container, dependency_definition from lagom.environment import Env ## Assume we have a Thing with a slightly different class in dev ## or maybe just in a different deployment class Thing(ABC): def run(self): ... class ProdVersionOfThing(Thing): def __init__(self, config): self.config = config def run(self): print(f"PROD: {self.config}") class DevVersionOfThing(Thing): def run(self): print(f"DEV THING") ## Lagom provides an env class to represent a logical ## grouping of environment variables class ThingEnvironment(Env): # This maps to an environment variable THING_CONN # Note: if the env variable is unset an error will be raised # as it is not marked as optional. thing_conn: str ## We define a dependency_definition function for ## our Thing which fetches the env from the container ## automatically loading the env variable. container = Container() @dependency_definition(container, singleton=True) def _thing_loader(c: Container) -> Thing: connection_string = c[ThingEnvironment].thing_conn if connection_string.startswith("dev://"): return DevVersionOfThing() return ProdVersionOfThing(connection_string) if __name__ == '__main__': thing = container[Thing] thing.run() ``` -------------------------------- ### Configure Request-Level Singletons in FastAPI Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Pass a list of types to `request_singletons` when creating `FastApiIntegration` to ensure these types are constructed only once per request. ```python deps = FastApiIntegration(container, request_singletons=[SomeClass]) ``` -------------------------------- ### Define Async Loaded Type Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Lagom supports async Python. Define async dependencies using `@dependency_definition` with an `async def` function. Retrieve them using `Awaitable`. ```python @dependency_definition(container) async def my_constructor() -> MyComplexDep: # await some stuff or any other async things return MyComplexDep(some_number=5) my_thing = await container[Awaitable[MyComplexDep]] ``` -------------------------------- ### Remove System Clock Dependency Source: https://github.com/meadsteve/lagom/blob/master/docs/cookbook.md Replace direct calls to `datetime.now()` with a bound dependency. This allows for easy mocking and testing by providing a specific datetime instance. ```python from datetime import datetime from typing import NewType from lagom import Container, magic_bind_to_container container = Container() Now = NewType("Now", datetime) # ↘€ lambda so it will be a new "now" each time it's needed container[Now] = lambda: datetime.now() @magic_bind_to_container(container) def month_as_a_string(now: Now): return now.strftime("%B") print(month_as_a_string()) # in testing: assert month_as_a_string(datetime(2020, 5, 12)) == "May" ``` -------------------------------- ### Define Dependency Construction in Lagom Source: https://github.com/meadsteve/lagom/blob/master/docs/explicit_definitions.md Explicitly define how dependencies are constructed by assigning a callable (like a lambda) or a `Singleton` wrapper to the dependency in the container. This ensures Lagom knows exactly how to build each object when it's requested. ```python from lagom import Singleton container[SomeClass] = lambda c: SomeClass(thing=c[AnotherDependency]) container[AnotherDependency] = Singleton(lambda c: AnotherDependency("Some Config")) ``` -------------------------------- ### Subtyping for Distinct Dependencies Source: https://github.com/meadsteve/lagom/blob/master/docs/next_steps.md Create subclasses to differentiate between dependency types, allowing for clearer type hinting and static analysis. ```python # Both these classes will behave exactly the same as Database, but we can now # indicate which type we need. class PrimaryDb(Database): pass class ReadReplica(Database): pass # Then configure them (they could be singletons or use any other lagom definitions) container[ReadReplica] = lambda: ReadReplica("read-replica-connection-string") container[PrimaryDb] = lambda: PrimaryDb("primary-connection-string") # The domain code then needs altering to hint which type of database class QueryService: def __init__(self, db: ReadReplica): pass class DataWriter: def __init__(self, db: PrimaryDb): pass ``` -------------------------------- ### Flask Blueprint Integration with Dependency Injection Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Integrate Flask Blueprints with Lagom's dependency injection container. This allows routes to automatically resolve dependencies like 'Database'. ```python from lagom.experimental.integrations.flask import FlaskBlueprintIntegration simple_page = Blueprint('simple_page', template_folder='templates') simple_page_with_deps = FlaskBlueprintIntegration(simple_page, container) @simple_page_with_deps.route("/save_it/", methods=['POST']) def save_to_db(thing_to_save, db: Database = injectable): db.save(thing_to_save) return 'saved' ``` -------------------------------- ### Define Environment Variables with Pydantic Source: https://github.com/meadsteve/lagom/blob/master/docs/next_steps.md Create Pydantic models inheriting from `Env` to define and map environment variables to class attributes. ```python class MyWebEnv(Env): port: str # maps to environment variable PORT host: str # maps to environment variable HOST class DBEnv(Env): db_host: str# maps to environment variable DB_HOST db_password: str# maps to environment variable DB_PASSWORD ``` -------------------------------- ### Integrate Lagom with Flask Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Use `FlaskIntegration` to wrap your Flask app and container, providing a decorated `route` function that automatically handles dependency injection for your Flask endpoints. ```python app = Flask(__name__) container[Database] = Singleton(lambda: Database("connection details")) app_with_deps = FlaskIntegration(app, container) @app_with_deps.route("/save_it/", methods=['POST']) def save_to_db(thing_to_save, db: Database = injectable): db.save(thing_to_save) return 'saved' ``` -------------------------------- ### Invocation Level Caching with Shared Dependencies Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Utilize the `shared` parameter in `magic_bind_to_container` to ensure classes are constructed only once per function invocation, acting as temporary singletons. This is useful for managing dependencies within the scope of a single request. ```python from lagom import magic_bind_to_container class ProfileLoader: def __init__(self, loader: DataLoader): pass class AvatarLoader: def __init__(self, loader: DataLoader): pass @magic_bind_to_container(container, shared=[DataLoader]) def handle_some_request(request: typing.Dict, profile: ProfileLoader, user_avatar: AvatarLoader): # do something to the game pass ``` -------------------------------- ### Define a context singleton dependency Source: https://github.com/meadsteve/lagom/blob/master/docs/clean_up.md Use `context_singletons` in `ContextContainer` to ensure only one instance of a dependency is created and cleaned up per invocation, even if referenced multiple times. This is ideal for resources like transactions that should be shared. ```python # Let's say we have two classes that both need an active transaction. # For an invocation we probably want these # to be the same class A: def __init__(self, t: Transaction): pass class B: def __init__(self, t: Transaction): pass @context_dependency_definition(container) def _get_a_transaction() -> Iterator[SomeDep]: transaction = Transaction() try: yield finally: transaction.commit() # Next we wrap the base container in a ContextContainer and configure SomeDep to be managed context_container = ContextContainer(container, context_types=[], context_singletons=[Transaction]) # Both a and b will get the same instance of a transaction because it was configured as a singleton # the cleanup will only happen once (per invocation). @bind_to_container(context_container) def do_something(a: A = injectable, b: B = injectable): print(f"do something") ``` -------------------------------- ### Configure Request-Context Singletons in FastAPI Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Use `request_context_singletons` to define types that are singletons for the request lifetime and require cleanup. These are implemented using Python context managers. ```python deps = FastApiIntegration(container, request_context_singletons=[SomeClass]) ``` -------------------------------- ### Generate Starlette Routes with Lagom Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Use StarletteIntegration to generate Starlette routes, automatically injecting dependencies defined in the Lagom container into your endpoint functions. ```python from lagom import injectable async def homepage(request, db: DBConnection = injectable): user = db.fetch_data_for_user(request.user) return PlainTextResponse(f"Hello {user.name}") container = Container() container[DBConnection] = DB("DSN_CONNECTION_GOES_HERE") with_deps = StarletteIntegration(container) routes = [ # This function takes the same arguments as starlette.routing.Route with_deps.route("/", endpoint=homepage), ] app = Starlette(routes=routes) ``` -------------------------------- ### Define Custom Construction with Lambda Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Provide a lambda function to the container to define how a type should be constructed. The lambda can optionally accept the container instance `c` for resolving nested dependencies. ```python # 👇 This lambda gets called each time container[SomeClass] = lambda: SomeClass("down", "spiral") ``` ```python # 👇 c is the lagom container container[SomeClass] = lambda c: SomeClass(c[SomeOtherDep], "spinning") ``` -------------------------------- ### Define a context-managed dependency Source: https://github.com/meadsteve/lagom/blob/master/docs/clean_up.md Use `@context_dependency_definition` to create a generator that yields a resource and cleans it up in the `finally` block. This is useful for dependencies that require explicit cleanup actions. ```python from lagom import Container, ContextContainer, context_dependency_definition, injectable, bind_to_container from typing import Iterator from .my_lib import SomeDep # First a regular lagom container is defined container = Container() # Then we create a ContextManager for some dependency SomeDep. # This would be available in Lagom as container[ContextManager[SomeDep]] @context_dependency_definition(container) def _load_dep_then_clean() -> Iterator[SomeDep]: try: yield SomeDep() finally: print("Clean up!!") # Next we wrap the base container in a ContextContainer and configure SomeDep to be managed context_container = ContextContainer(container, context_types=[SomeDep]) # Then our functions can be bound to this ContextContainer # At the end of invoking this function the cleanup code of SomeDep will be automatically called. @bind_to_container(context_container) def do_something(dep: SomeDep = injectable): print(f"I got {dep}") ``` -------------------------------- ### Use Existing ContextManager for Dependency Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md If a `ContextManager` already exists for a class, it can be directly used to define a context-dependent dependency in the container. ```python class SomeResourceManager: def __enter__(self): return SomeResource() def __exit__(self, exc_type, exc_val, exc_tb): # TODO: do some tidy up now the request has finished pass container[ContextManager[SomeResource]] = SomeResourceManager ``` -------------------------------- ### Bind Explicit Arguments with Injectable Marker Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Use the `injectable` marker with `bind_to_container` to explicitly control which arguments Lagom injects. Lagom will only attempt to inject arguments marked with `injectable` if they are not provided by the caller. ```python from lagom import injectable import typing @bind_to_container(container) def handle_some_request(request: typing.Dict, profile: ProfileLoader = injectable, user_avatar: AvatarLoader = injectable): # do something to the game pass ``` -------------------------------- ### Override Dependencies for FastAPI Testing Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Use `deps.override_for_test()` to temporarily swap dependencies in the Lagom container when testing with FastAPI's test client. This is useful for mocking external services. ```python def test_something(): client = TestClient(app) with deps.override_for_test() as test_container: # FooService is an external API so mock it during test test_container[FooService] = Mock(FooService) response = client.get("/") assert response.status_code == 200 ``` -------------------------------- ### Hooking Functions to Container Source: https://github.com/meadsteve/lagom/blob/master/README.md Use the `bind_to_container` decorator to make top-level functions accessible via the container. Use `lagom.injectable` for optional dependencies. ```python @bind_to_container(container) def handle_move_post_request(request: typing.Dict, game: Game = lagom.injectable): # do something to the game return Response() ``` -------------------------------- ### Use a Test Container in a Test Source: https://github.com/meadsteve/lagom/blob/master/docs/testing_with_lagom.md Utilize a test container fixture within your tests. You can further modify dependencies within the test function itself before retrieving components. ```python def test_something(container_fixture: Container): container_fixture[DiceClient] = FakeDice(always_roll=6) game_to_test = container_fixture[Game] # TODO: act & assert on something ``` -------------------------------- ### Define Custom Construction with Function Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Use the `@dependency_definition` decorator to bind a function to the container for constructing a type, suitable for more complex logic. ```python @dependency_definition(container) def my_constructor() -> MyComplexDep: # Really long # stuff goes here return MyComplexDep(some_number=5) ``` -------------------------------- ### Django Settings Injection Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Inject Django settings into your application components. The `DjangoSettings` class automatically provides access to your app's settings. ```python # settings.py SECRET_MSG = "hello world" # views.py @dependencies.bind_view def show_secret_message(request, settings: DjangoSettings = injectable): return HttpResponse(f"your secret message is: {settings.SECRET_MSG}") ``` -------------------------------- ### Run Specific Pytest Commands Source: https://github.com/meadsteve/lagom/blob/master/CLAUDE.md When not using `make` targets, run specific pytest commands within the pipenv environment. This allows for targeted execution of tests by file or specific test name. ```bash pipenv run pytest tests/path/to/test_file.py -vv pipenv run pytest tests/path/to/test_file.py::test_name -vv ``` -------------------------------- ### Inject Environment Variables into Functions Source: https://github.com/meadsteve/lagom/blob/master/docs/next_steps.md Use `magic_bind_to_container` to inject environment variables defined by Pydantic models into function arguments. ```python # Example usage: # DB_HOST=localhost DB_PASSWORD=secret python myscript.py c = Container() @magic_bind_to_container(c) def main(env: DBEnv): print(f"Config supplied: {env.db_host}, {env.db_password}") if __name__ == "__main__": main() ``` -------------------------------- ### Alias Concrete Class to ABC Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Map an Abstract Base Class (ABC) or interface to a specific concrete class at runtime. ```python container[SomeAbc] = ConcreteClass ``` -------------------------------- ### Define Context Dependency with Generator Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md Define a context-dependent dependency using a generator decorated with `@context_dependency_definition`. The `finally` block is executed after the request is handled for cleanup. ```python @context_dependency_definition(container) def my_constructor() -> Iterator[SomeResource]: try: yield SomeResource() finally: # TODO: do some tidy up now the request has finished pass ``` -------------------------------- ### Prevent Automatic Construction Source: https://github.com/meadsteve/lagom/blob/master/docs/index.md Configure a type to raise an error if Lagom attempts to construct it, using `UnresolvableTypeDefinition`. ```python container[SomeDep] = UnresolvableTypeDefinition("You can't resolve SomeDep because reason") ``` -------------------------------- ### Access Request Object in FastAPI with Lagom Source: https://github.com/meadsteve/lagom/blob/master/docs/framework_integrations.md The FastAPI integration automatically binds the active request to the container, enabling you to inject the `starlette.requests.Request` object into your classes. ```python from starlette.requests import Request class SomeExtendedRequest: def __init__(self, req: Request, db: Database): pass ``` -------------------------------- ### Django View with Injectable Models Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Use injectable Django models within views. This provides access to model managers and creation methods, abstracting away direct model references. ```python from django.http import HttpResponse from django.utils import timezone from lagom.experimental.integrations.django import DjangoModel from .dependency_config import dependencies from .models import Question @dependencies.bind_view def new_question(request, questions: DjangoModel[Question]): new_question = questions.new(question_text="What's next?", pub_date=timezone.now()) new_question.save() return HttpResponse(f"new question created") @dependencies.bind_view def question_count(request, questions: DjangoModel[Question]): count = questions.objects.all().count() return HttpResponse(f"{count} questions are in the DB") ``` -------------------------------- ### Django View Binding with Dependency Injection Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Bind Django views to the dependency injection container. This allows services to be injected directly into view functions or class-based views. ```python from .dependency_config import dependencies @dependencies.bind_view def index(request, dep: SomeService): return HttpResponse(f"service says: {dep.get_message()}") # Or if you prefer class based views @dependencies.bind_view class CBVexample(View): def get(self, request, dep: SomeService): return HttpResponse(f"service says: {dep.get_message()}") ``` -------------------------------- ### Injecting HttpRequest into Services Source: https://github.com/meadsteve/lagom/blob/master/docs/experimental.md Inject the current `HttpRequest` object into services. This allows services to access request-specific data when they are constructed. ```python # some_services.py class DataStoreForRequest: def __init__(self, request: HttpRequest, db: SomeDb): # Now we can use anything generic we want from the request pass # views.py @dependencies.bind_view def question_count(request, store: DataStoreForRequest = injectable): # The `store` here was constructed with the correct request object return HttpResponse(f"Done") ``` -------------------------------- ### Hide Executor Details from Functions Source: https://github.com/meadsteve/lagom/blob/master/docs/cookbook.md Abstract away executor details like `ThreadPoolExecutor` by binding it to a `NewType`. This allows functions to depend on an abstract executor type without knowing the concrete implementation. ```python import asyncio import concurrent from concurrent.futures.thread import ThreadPoolExecutor from typing import NewType from lagom import Container, magic_bind_to_container container = Container() IOBoundExecutor = NewType("IOBoundExecutor", ThreadPoolExecutor) MAX_WORKERS = 8 # Or twice CPU count or whatever container[IOBoundExecutor] = lambda : ThreadPoolExecutor(max_workers=MAX_WORKERS) # This function now doesn't need to know details of the # system it's running on (CPU count). All it needs to know # is if the task is IO bound or not. @magic_bind_to_container(container) def do_some_work(io_bound_executor: IOBoundExecutor): with io_bound_executor as executor: futures = {executor.submit(process_message, n) for n in range(0, 20)} completed_futures = concurrent.futures.wait(futures) print([f.result() for f in completed_futures.done]) def process_message(number): # Pretend we did some io bound work return f"hej {number}" asyncio.run(do_some_work()) ```