mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-07-10 16:12:26 +03:00
Add DependenciesContainer provider
This commit is contained in:
parent
af28cb60c2
commit
926f716ce6
8
examples/miniapps/bundles_v2/README.rst
Normal file
8
examples/miniapps/bundles_v2/README.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
Dependency Injector Bundles example
|
||||
===================================
|
||||
|
||||
Instructions for running
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python run.py
|
1
examples/miniapps/bundles_v2/bundles/__init__.py
Normal file
1
examples/miniapps/bundles_v2/bundles/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Bundles package."""
|
19
examples/miniapps/bundles_v2/bundles/photos/__init__.py
Normal file
19
examples/miniapps/bundles_v2/bundles/photos/__init__.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
"""Photos bundle."""
|
||||
|
||||
from dependency_injector import containers
|
||||
from dependency_injector import providers
|
||||
|
||||
from . import entities
|
||||
from . import repositories
|
||||
|
||||
|
||||
class Photos(containers.DeclarativeContainer):
|
||||
"""Photos bundle container."""
|
||||
|
||||
core = providers.DependenciesContainer()
|
||||
|
||||
photo = providers.Factory(entities.Photo)
|
||||
photo_repository = providers.Singleton(repositories.PhotoRepository,
|
||||
object_factory=photo.provider,
|
||||
fs=core.file_storage,
|
||||
db=core.database)
|
5
examples/miniapps/bundles_v2/bundles/photos/entities.py
Normal file
5
examples/miniapps/bundles_v2/bundles/photos/entities.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
"""Photos bundle entities module."""
|
||||
|
||||
|
||||
class Photo(object):
|
||||
"""Photo entity."""
|
11
examples/miniapps/bundles_v2/bundles/photos/repositories.py
Normal file
11
examples/miniapps/bundles_v2/bundles/photos/repositories.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
"""Photos bundle entity repositories module."""
|
||||
|
||||
|
||||
class PhotoRepository(object):
|
||||
"""Photo entity repository."""
|
||||
|
||||
def __init__(self, object_factory, fs, db):
|
||||
"""Initializer."""
|
||||
self.object_factory = object_factory
|
||||
self.fs = fs
|
||||
self.db = db
|
18
examples/miniapps/bundles_v2/bundles/users/__init__.py
Normal file
18
examples/miniapps/bundles_v2/bundles/users/__init__.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
"""Users bundle."""
|
||||
|
||||
from dependency_injector import containers
|
||||
from dependency_injector import providers
|
||||
|
||||
from . import entities
|
||||
from . import repositories
|
||||
|
||||
|
||||
class Users(containers.DeclarativeContainer):
|
||||
"""Users bundle container."""
|
||||
|
||||
core = providers.DependenciesContainer()
|
||||
|
||||
user = providers.Factory(entities.User)
|
||||
user_repository = providers.Singleton(repositories.UserRepository,
|
||||
object_factory=user.provider,
|
||||
db=core.database)
|
9
examples/miniapps/bundles_v2/bundles/users/entities.py
Normal file
9
examples/miniapps/bundles_v2/bundles/users/entities.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
"""Users bundle entities module."""
|
||||
|
||||
|
||||
class User(object):
|
||||
"""User entity."""
|
||||
|
||||
def __init__(self, id):
|
||||
"""Initializer."""
|
||||
self.id = id
|
14
examples/miniapps/bundles_v2/bundles/users/repositories.py
Normal file
14
examples/miniapps/bundles_v2/bundles/users/repositories.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""Users bundle entity repositories module."""
|
||||
|
||||
|
||||
class UserRepository(object):
|
||||
"""User entity repository."""
|
||||
|
||||
def __init__(self, object_factory, db):
|
||||
"""Initializer."""
|
||||
self.object_factory = object_factory
|
||||
self.db = db
|
||||
|
||||
def get(self, id):
|
||||
"""Return user entity with given identifier."""
|
||||
return self.object_factory(id=id)
|
41
examples/miniapps/bundles_v2/run.py
Normal file
41
examples/miniapps/bundles_v2/run.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
"""Run 'Bundles' example application."""
|
||||
|
||||
import sqlite3
|
||||
import boto3
|
||||
|
||||
from dependency_injector import containers
|
||||
from dependency_injector import providers
|
||||
|
||||
from bundles.users import Users
|
||||
from bundles.photos import Photos
|
||||
|
||||
|
||||
class Core(containers.DeclarativeContainer):
|
||||
"""Core container."""
|
||||
|
||||
config = providers.Configuration('config')
|
||||
database = providers.Singleton(sqlite3.connect, config.database.dsn)
|
||||
file_storage = providers.Singleton(
|
||||
boto3.client, 's3',
|
||||
aws_access_key_id=config.aws.access_key_id,
|
||||
aws_secret_access_key=config.aws.secret_access_key)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Initializing containers
|
||||
core = Core()
|
||||
core.config.update({'database': {'dsn': ':memory:'},
|
||||
'aws': {'access_key_id': 'KEY',
|
||||
'secret_access_key': 'SECRET'}})
|
||||
users = Users(core=core)
|
||||
photos = Photos(core=core)
|
||||
|
||||
# Fetching few users
|
||||
user_repository = users.user_repository()
|
||||
user1 = user_repository.get(id=1)
|
||||
user2 = user_repository.get(id=2)
|
||||
|
||||
# Making some checks
|
||||
assert user1.id == 1
|
||||
assert user2.id == 2
|
||||
assert user_repository.db is core.database()
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -13,6 +13,7 @@ cdef class Provider(object):
|
|||
cdef object __overriding_lock
|
||||
|
||||
cpdef object _provide(self, tuple args, dict kwargs)
|
||||
cpdef void _copy_overridings(self, Provider copied)
|
||||
|
||||
|
||||
cdef class Object(Provider):
|
||||
|
@ -33,6 +34,12 @@ cdef class ExternalDependency(Dependency):
|
|||
pass
|
||||
|
||||
|
||||
cdef class DependenciesContainer(Object):
|
||||
cdef dict __providers
|
||||
|
||||
cpdef object _override_providers(self, object container)
|
||||
|
||||
|
||||
cdef class OverridingContext(object):
|
||||
cdef Provider __overridden
|
||||
cdef Provider __overriding
|
||||
|
|
|
@ -107,8 +107,7 @@ cdef class Provider(object):
|
|||
|
||||
copied = self.__class__()
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
@ -215,6 +214,11 @@ cdef class Provider(object):
|
|||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
cpdef void _copy_overridings(self, Provider copied):
|
||||
"""Copy provider overridings to a newly copied provider."""
|
||||
copied.__overridden = deepcopy(self.__overridden)
|
||||
copied.__last_overriding = deepcopy(self.__last_overriding)
|
||||
|
||||
|
||||
cdef class Object(Provider):
|
||||
"""Object provider returns provided instance "as is".
|
||||
|
@ -243,8 +247,7 @@ cdef class Object(Provider):
|
|||
|
||||
copied = self.__class__(deepcopy(self.__provides, memo))
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
@ -330,8 +333,7 @@ cdef class Dependency(Provider):
|
|||
|
||||
copied = self.__class__(self.__instance_of)
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
@ -418,6 +420,121 @@ cdef class ExternalDependency(Dependency):
|
|||
"""
|
||||
|
||||
|
||||
cdef class DependenciesContainer(Object):
|
||||
""":py:class:`DependenciesContainer` provider provides set of dependencies.
|
||||
|
||||
|
||||
Dependencies container provider is used to implement late static binding
|
||||
for a set of providers of a particular container.
|
||||
|
||||
|
||||
Example code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class Adapters(containers.DeclarativeContainer):
|
||||
email_sender = providers.Singleton(SmtpEmailSender)
|
||||
|
||||
class TestAdapters(containers.DeclarativeContainer):
|
||||
email_sender = providers.Singleton(EchoEmailSender)
|
||||
|
||||
class UseCases(containers.DeclarativeContainer):
|
||||
adapters = providers.DependenciesContainer()
|
||||
|
||||
signup = providers.Factory(SignupUseCase,
|
||||
email_sender=adapters.email_sender)
|
||||
|
||||
use_cases = UseCases(adapters=Adapters)
|
||||
# or
|
||||
use_cases = UseCases(adapters=TestAdapters)
|
||||
|
||||
# Another file
|
||||
from .containers import use_cases
|
||||
|
||||
use_case = use_cases.signup()
|
||||
use_case.execute()
|
||||
"""
|
||||
|
||||
def __init__(self, provides=None, **dependencies):
|
||||
"""Initializer."""
|
||||
self.__providers = dependencies
|
||||
|
||||
if provides:
|
||||
self._override_providers(container=provides)
|
||||
|
||||
super(DependenciesContainer, self).__init__(provides)
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
"""Create and return full copy of provider."""
|
||||
cdef DependenciesContainer copied
|
||||
|
||||
copied = memo.get(id(self))
|
||||
if copied is not None:
|
||||
return copied
|
||||
|
||||
copied = self.__class__()
|
||||
copied.__provides = deepcopy(self.__provides, memo)
|
||||
copied.__providers = deepcopy(self.__providers, memo)
|
||||
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Return dependency provider."""
|
||||
if name.startswith('__') and name.endswith('__'):
|
||||
raise AttributeError(
|
||||
'\'{cls}\' object has no attribute '
|
||||
'\'{attribute_name}\''.format(cls=self.__class__.__name__,
|
||||
attribute_name=name))
|
||||
|
||||
provider = self.__providers.get(name)
|
||||
if not provider:
|
||||
provider = Dependency()
|
||||
self.__providers[name] = provider
|
||||
|
||||
container = self.__call__()
|
||||
if container:
|
||||
dependency_provider = container.providers.get(name)
|
||||
if dependency_provider:
|
||||
provider.override(dependency_provider)
|
||||
|
||||
return provider
|
||||
|
||||
@property
|
||||
def providers(self):
|
||||
"""Read-only dictionary of dependency providers."""
|
||||
return self.__providers
|
||||
|
||||
def override(self, provider):
|
||||
"""Override provider with another provider.
|
||||
|
||||
:param provider: Overriding provider.
|
||||
:type provider: :py:class:`Provider`
|
||||
|
||||
:raise: :py:exc:`dependency_injector.errors.Error`
|
||||
|
||||
:return: Overriding context.
|
||||
:rtype: :py:class:`OverridingContext`
|
||||
"""
|
||||
self._override_providers(container=provider)
|
||||
return super(DependenciesContainer, self).override(provider)
|
||||
|
||||
|
||||
cpdef object _override_providers(self, object container):
|
||||
"""Override providers with providers from provided container."""
|
||||
for name, dependency_provider in container.providers.items():
|
||||
provider = self.__providers.get(name)
|
||||
|
||||
if not provider:
|
||||
continue
|
||||
|
||||
if provider.last_overriding is dependency_provider:
|
||||
continue
|
||||
|
||||
provider.override(dependency_provider)
|
||||
|
||||
|
||||
cdef class OverridingContext(object):
|
||||
"""Provider overriding context.
|
||||
|
||||
|
@ -517,8 +634,7 @@ cdef class Callable(Provider):
|
|||
*deepcopy(self.args, memo),
|
||||
**deepcopy(self.kwargs, memo))
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
@ -755,8 +871,7 @@ cdef class Configuration(Provider):
|
|||
copied.__value = deepcopy(self.__value, memo)
|
||||
copied.__children = deepcopy(self.__children, memo)
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
@ -923,8 +1038,7 @@ cdef class Factory(Provider):
|
|||
**deepcopy(self.kwargs, memo))
|
||||
copied.set_attributes(**deepcopy(self.attributes, memo))
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
@ -1272,8 +1386,7 @@ cdef class BaseSingleton(Provider):
|
|||
**deepcopy(self.kwargs, memo))
|
||||
copied.set_attributes(**deepcopy(self.attributes, memo))
|
||||
|
||||
for overriding_provider in self.overridden:
|
||||
copied.override(deepcopy(overriding_provider, memo))
|
||||
self._copy_overridings(copied)
|
||||
|
||||
return copied
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import unittest2 as unittest
|
||||
|
||||
from dependency_injector import (
|
||||
containers,
|
||||
providers,
|
||||
errors,
|
||||
)
|
||||
|
@ -298,3 +299,52 @@ class ExternalDependencyTests(unittest.TestCase):
|
|||
|
||||
def test_is_instance(self):
|
||||
self.assertIsInstance(self.provider, providers.Dependency)
|
||||
|
||||
|
||||
class DependenciesContainerTests(unittest.TestCase):
|
||||
|
||||
class Container(containers.DeclarativeContainer):
|
||||
|
||||
dependency = providers.Provider()
|
||||
|
||||
def setUp(self):
|
||||
self.provider = providers.DependenciesContainer()
|
||||
self.container = self.Container()
|
||||
|
||||
def test_getattr(self):
|
||||
has_dependency = hasattr(self.provider, 'dependency')
|
||||
dependency = self.provider.dependency
|
||||
|
||||
self.assertIsInstance(dependency, providers.Dependency)
|
||||
self.assertIs(dependency, self.provider.dependency)
|
||||
self.assertTrue(has_dependency)
|
||||
self.assertIsNone(dependency.last_overriding)
|
||||
|
||||
def test_getattr_with_container(self):
|
||||
self.provider.override(self.container)
|
||||
|
||||
dependency = self.provider.dependency
|
||||
|
||||
self.assertTrue(dependency.overridden)
|
||||
self.assertIs(dependency.last_overriding, self.container.dependency)
|
||||
|
||||
def test_providers(self):
|
||||
dependency1 = self.provider.dependency1
|
||||
dependency2 = self.provider.dependency2
|
||||
self.assertEqual(self.provider.providers, {'dependency1': dependency1,
|
||||
'dependency2': dependency2})
|
||||
|
||||
def test_override(self):
|
||||
dependency = self.provider.dependency
|
||||
self.provider.override(self.container)
|
||||
|
||||
self.assertTrue(dependency.overridden)
|
||||
self.assertIs(dependency.last_overriding, self.container.dependency)
|
||||
|
||||
def test_init_with_container_and_providers(self):
|
||||
provider = providers.DependenciesContainer(
|
||||
self.container, dependency=providers.Dependency())
|
||||
dependency = provider.dependency
|
||||
|
||||
self.assertTrue(dependency.overridden)
|
||||
self.assertIs(dependency.last_overriding, self.container.dependency)
|
||||
|
|
Loading…
Reference in New Issue
Block a user