Funq dependency injection container port for python
pip install py-funq
from abc import ABC, abstractmethod
class Developer(ABC):
@abstractmethod
def code(self) -> str:
pass
class PythonDeveloper(Developer):
def code(self) -> str:
return 'Python'
class Person:
def __init__(self, developer: Developer):
self._developer = developer
from pyfunq import Container
container = Container()
container.register(Developer, lambda c: PythonDeveloper())
container.configure()
resolved = container.resolve(Developer)
types that don't have dependencies can be auto registered
container.register(PythonDeveloper)
container.configure()
container.resolve(PythonDeveloper)
container.register(Person, (
lambda c: Person(c.resolve(PythonDeveloper)))
)
container.configure()
person = container.resolve(Person)
container.register(Developer, lambda c: PythonDeveloper()) \
.named('foo')
container.register(Developer, lambda c: PythonDeveloper())
container.configure()
developer = container.resolve(Developer)
named_developer = container.resolve_named(Developer, 'foo')
developer is not named_developer # True
The container will throw an exception if it will not be able to resolve a dependency.
You can use the try_resolve
or try_resolve_named
functions in order to avoid an exception:
container = Container()
container.configure()
developer = container.try_resolve(Developer)
if developer is not None:
...
namedDeveloper = container.try_resolve_named(Developer, 'foo')
if namedDeveloper is not None:
...
You can register a dependency that accepts any number of arguments
class OneArgumentClass:
def __init__(self, arg1: str):
self._arg1 = arg1
class TwoArgumentsClass:
def __init__(self, arg1: str, arg2: int):
self._arg1 = arg1
self._arg2 = arg2
class ThreeArgumentsClass:
def __init__(self, arg1: str, arg2: int, arg3: bool):
self._arg1 = arg1
self._arg2 = arg2
self._arg3 = arg3
container = Container()
container.register([OneArgumentClass, str], lambda c, arg1: OneArgumentClass(arg1))
container.register([TwoArgumentsClass, str, int], lambda c, arg1, arg2: TwoArgumentsClass(arg1, arg2))
container.register([ThreeArgumentsClass, str, int, bool], lambda c, arg1, arg2, arg3: ThreeArgumentsClass(arg1, arg2, arg3))
And resolve the dependencies using the appropriate arguments
container.configure()
container.resolve(OneArgumentClass, 'value')
container.resolve(TwoArgumentsClass, 'value', 10)
container.resolve(ThreeArgumentsClass, 'value', 10, True)
By default all child containers can resolve dependencies within themselves and their parent.
container = Container()
child_container = container.create_child_container()
container.register(Developer, lambda c: PythonDeveloper())
child_container.configure()
child_container.resolve(Developer)
try:
container.resolve(Developer)
except ResolutionError:
assert True
The lifetime of an instance can be a singleton or per call (transient)
You can control the lifetime using the ReuseScope
enum.
from enum import Enum
class ReuseScope(Enum):
NoReuse = 'NoReuse'
Container = 'Container'
Hierarchy = 'Hierarchy'
By default all registrations are marked using the ReuseScope.container
scope.
from pyfunq.reuse_scope import ReuseScope
# transient
container.register(Developer, lambda c: PythonDeveloper()) \
.reused_within(ReuseScope.NoReuse)
# singleton
container.register(Developer, lambda c: PythonDeveloper()) \
.reused_within(ReuseScope.Hierarchy)
# singleton per container
container.register(Developer, lambda c: PythonDeveloper()) \
.reused_within(ReuseScope.Container)
You can let the container handle disposal of instances using the Owner
enum.
from enum import Enum
class Owner(Enum):
External = 'External'
Container = 'Container'
Only context manager instances can be managed within the container.
By default, all registrations are marked using the Owner.External
scope.
from pyfunq.owner import Owner
class Disposable:
def __init__(self):
self._is_disposed = False
@property
def is_disposed(self) -> bool:
return self._is_disposed
def __enter__(self):
return self
def __exit__(self, ex_type, value, traceback):
self._is_disposed = True
container = Container()
# The container is not responsible for disposing the instance
# container.register(PythonDeveloper).owned_by(Owner.External)
# or
# container.register(PythonDeveloper)
# The container is responsible for disposing the instance
container.register(Disposable).owned_by(Owner.Container)
container.configure()
disposable = container.resolve(Disposable)
container.dispose()
assert disposable._is_disposed
disposable: Disposable
with Container() as container:
container.register(Disposable).owned_by(Owner.Container)
container.configure()
disposable = container.resolve(Disposable)
assert disposable._is_disposed
container = Container()
container.default_owner = Owner.External
container.default_reuse = ReuseScope.NoReuse
MIT
Copyright © 2023 foldl