Lagom, a type based dependency injection container
Project description
Lagom - Dependency injection container
Usage
Everything in Lagom is based on types. To create an object you pass the type to the container:
container = Container()
some_thing = container[SomeClass]
Defining a singleton
container[SomeExpensiveToCreateClass] = SomeExpensiveToCreateClass("up", "left")
alternatively if you want to defer construction until it's needed:
container[SomeExpensiveToCreateClass] = Singleton(SomeExpensiveToCreateClass)
Defining a type that gets recreated every time
container[SomeClass] = lambda: SomeClass("down", "spiral")
if the type needs things from the container the lambda can take a single argument which is the container:
container[SomeClass] = lambda c: SomeClass(c[SomeOtherDep], "spinning")
if your construction logic is longer than would fit in a lambda a function can also be bound to the container:
@dependency_definition(container)
def my_constructor() -> MyComplexDep:
# Really long
# stuff goes here
return MyComplexDep(some_number=5)
Alias a concrete instance to an ABC
container[SomeAbc] = ConcreteClass
Partially bind a function
Apply a function decorator to any function.
@bind_to_container(container)
def handle_some_request(request: typing.Dict, game: Game):
# do something to the game
pass
This function can now be called omitting any arguments that the container knows how to build.
# we can now call the following. the game argument will automagically
# come from the container
handle_some_request(request={"roll_dice": 5})
Invocation level caching
Suppose you have a function and you want all the dependencies to share an instance of an object then you can define invocation level shared dependencies.
class ProfileLoader:
def __init__(self, loader: DataLoader):
pass
class AvatarLoader:
def __init__(self, loader: DataLoader):
pass
@bind_to_container(container, shared=[DataLoader])
def handle_some_request(request: typing.Dict, profile: ProfileLoader, user_avatar: AvatarLoader):
# do something to the game
pass
now each invocation of handle_some_request will get the same instance of loader so this class can cache values for the invocation lifetime.
Full Example
App setup
from abc import ABC
from dataclasses import dataclass
from lagom import Container
#--------------------------------------------------------------
# Here is an example of some classes your application may be built from
@dataclass
class DiceApiUrl:
url: 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]
Testing without patching
Taking the container from above we can now swap out
the dice client to a test double/fake. When we get an
instance of the Game
class it will have the new
fake dice client injected in.
def some_test(container: Container):
container[DiceClient] = FakeDice(always_roll=6)
game_to_test = container[Game]
# TODO: act & assert on something
Design Goals
- Everything should be done by type. No reliance on names.
- All domain code should remain unmodified. No special decorators.
- Make use of modern python features (3.7 at the time of creation)
- The API should expose sensible typing (for use in pycharm/mypy)
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for lagom-0.5.1-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 94d016240aa04bba4aac2e4966805015b91125210fde7842536038a182d5176f |
|
MD5 | 26a66b7a398cb5cb89ad85b2c92ba96c |
|
BLAKE2b-256 | 07885cdd1023515f1c0488ebf40a9b021b3ad469e87365ce0e12c7efaf5154ab |