Skip to content

Commit 5e55317

Browse files
committed
Merge branch 'release/4.20.0' into master
2 parents ce6d3df + 6b24cb8 commit 5e55317

File tree

14 files changed

+12901
-9078
lines changed

14 files changed

+12901
-9078
lines changed

docs/containers/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Containers module API docs - :py:mod:`dependency_injector.containers`.
2222
declarative
2323
dynamic
2424
specialization
25+
inject_self
2526
overriding
2627
reset_singletons
2728
traversal

docs/containers/inject_self.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Injecting container "self"
2+
==========================
3+
4+
You can inject container "self" into container providers.
5+
6+
.. literalinclude:: ../../examples/containers/inject_self.py
7+
:language: python
8+
:lines: 3-
9+
:emphasize-lines: 20, 26
10+
11+
To inject container "self" you need to define ``Self`` provider. Container can have only one ``Self`` provider.
12+
13+
Usually you will use name ``__self__``.
14+
You can also use different name. When you use different name container will also reference
15+
defined ``Self`` provider in ``.__self__`` attribute.
16+
17+
Provider ``Self`` is not listed in container ``.providers`` attributes.
18+
19+
.. disqus::
20+

docs/main/changelog.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ that were made in every particular version.
77
From version 0.7.6 *Dependency Injector* framework strictly
88
follows `Semantic versioning`_
99

10+
4.20.0
11+
------
12+
- Add container "self" injections.
13+
See issue: `#364 <https://github.com/ets-labs/python-dependency-injector/issues/364>`_.
14+
Thanks to `@shaunc <https://github.com/shaunc>`_ for suggesting the feature.
15+
1016
4.19.0
1117
------
1218
- Add ``singleton.full_reset()`` method to reset all underlying singleton providers.

examples/containers/inject_self.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"""Container injecting ``self`` example."""
2+
3+
from dependency_injector import containers, providers
4+
5+
6+
class Service:
7+
def __init__(self, name: str):
8+
self.name = name
9+
10+
11+
class ServiceDispatcher:
12+
def __init__(self, container: containers.Container):
13+
self.container = container
14+
15+
def get_services(self):
16+
for provider in self.container.traverse(types=[providers.Factory]):
17+
yield provider()
18+
19+
20+
class Container(containers.DeclarativeContainer):
21+
22+
__self__ = providers.Self()
23+
24+
service1 = providers.Factory(Service, name='Service 1')
25+
service2 = providers.Factory(Service, name='Service 2')
26+
service3 = providers.Factory(Service, name='Service 3')
27+
28+
dispatcher = providers.Singleton(ServiceDispatcher, __self__)
29+
30+
31+
if __name__ == '__main__':
32+
container = Container()
33+
34+
dispatcher = container.dispatcher()
35+
for service in dispatcher.get_services():
36+
print(service.name)

src/dependency_injector/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Top-level package."""
22

3-
__version__ = '4.19.0'
3+
__version__ = '4.20.0'
44
"""Version number.
55
66
:type: str

src/dependency_injector/containers.c

Lines changed: 3572 additions & 2207 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/dependency_injector/containers.pyi

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,13 @@ from typing import (
77
Union,
88
ClassVar,
99
Callable as _Callable,
10-
Sequence,
1110
Iterable,
12-
Iterator,
1311
TypeVar,
1412
Awaitable,
1513
overload,
1614
)
1715

18-
from .providers import Provider
16+
from .providers import Provider, Self
1917

2018

2119
C_Base = TypeVar('C_Base', bound='Container')
@@ -29,12 +27,13 @@ class Container:
2927
providers: Dict[str, Provider]
3028
dependencies: Dict[str, Provider]
3129
overridden: Tuple[Provider]
32-
__self__: Provider
30+
__self__: Self
3331
def __init__(self) -> None: ...
3432
def __deepcopy__(self, memo: Optional[Dict[str, Any]]) -> Provider: ...
3533
def __setattr__(self, name: str, value: Union[Provider, Any]) -> None: ...
3634
def __delattr__(self, name: str) -> None: ...
3735
def set_providers(self, **providers: Provider): ...
36+
def set_provider(self, name: str, provider: Provider) -> None: ...
3837
def override(self, overriding: C_Base) -> None: ...
3938
def override_providers(self, **overriding_providers: Provider) -> None: ...
4039
def reset_last_overriding(self) -> None: ...
@@ -47,10 +46,10 @@ class Container:
4746
def apply_container_providers_overridings(self) -> None: ...
4847
def reset_singletons(self) -> None: ...
4948
@overload
50-
def traverse(self, types: Optional[Sequence[TT]] = None) -> Iterator[TT]: ...
49+
def traverse(self, types: Optional[Iterable[Type[TT]]] = None) -> _Iterator[TT]: ...
5150
@classmethod
5251
@overload
53-
def traverse(cls, types: Optional[Sequence[TT]] = None) -> Iterator[TT]: ...
52+
def traverse(self, types: Optional[Iterable[Type[TT]]] = None) -> _Iterator[TT]: ...
5453

5554

5655
class DynamicContainer(Container): ...

src/dependency_injector/containers.pyx

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class DynamicContainer(Container):
6969
self.declarative_parent = None
7070
self.wired_to_modules = []
7171
self.wired_to_packages = []
72-
self.__self__ = providers.Object(self)
72+
self.__self__ = providers.Self(self)
7373
super(DynamicContainer, self).__init__()
7474

7575
def __deepcopy__(self, memo):
@@ -79,12 +79,18 @@ class DynamicContainer(Container):
7979
return copied
8080

8181
copied = self.__class__()
82+
memo[id(self)] = copied
83+
8284
copied.provider_type = providers.Provider
8385
copied.overridden = providers.deepcopy(self.overridden, memo)
8486
copied.declarative_parent = self.declarative_parent
8587

88+
copied.__self__ = providers.deepcopy(self.__self__, memo)
89+
for name in copied.__self__.alt_names:
90+
copied.set_provider(name, copied.__self__)
91+
8692
for name, provider in providers.deepcopy(self.providers, memo).items():
87-
setattr(copied, name, provider)
93+
copied.set_provider(name, provider)
8894

8995
return copied
9096

@@ -102,7 +108,7 @@ class DynamicContainer(Container):
102108
103109
:rtype: None
104110
"""
105-
if isinstance(value, providers.Provider) and name != '__self__':
111+
if isinstance(value, providers.Provider) and not isinstance(value, providers.Self):
106112
_check_provider_type(self, value)
107113
self.providers[name] = value
108114
super(DynamicContainer, self).__setattr__(name, value)
@@ -154,6 +160,19 @@ class DynamicContainer(Container):
154160
for name, provider in six.iteritems(providers):
155161
setattr(self, name, provider)
156162

163+
def set_provider(self, name, provider):
164+
"""Set container provider.
165+
166+
:param name: Provider name
167+
:type name: str
168+
169+
:param provider: Provider
170+
:type provider: :py:class:`dependency_injector.providers.Provider`
171+
172+
:rtype: None
173+
"""
174+
setattr(self, name, provider)
175+
157176
def override(self, object overriding):
158177
"""Override current container by overriding container.
159178
@@ -282,6 +301,10 @@ class DeclarativeContainerMetaClass(type):
282301

283302
def __new__(type mcs, str class_name, tuple bases, dict attributes):
284303
"""Declarative container class factory."""
304+
self = mcs.__fetch_self(attributes)
305+
if self is None:
306+
self = providers.Self()
307+
285308
containers = {
286309
name: container
287310
for name, container in six.iteritems(attributes)
@@ -291,7 +314,7 @@ class DeclarativeContainerMetaClass(type):
291314
cls_providers = {
292315
name: provider
293316
for name, provider in six.iteritems(attributes)
294-
if isinstance(provider, providers.Provider)
317+
if isinstance(provider, providers.Provider) and not isinstance(provider, providers.Self)
295318
}
296319

297320
inherited_providers = {
@@ -312,7 +335,8 @@ class DeclarativeContainerMetaClass(type):
312335

313336
cls = <type>type.__new__(mcs, class_name, bases, attributes)
314337

315-
cls.__self__ = providers.Object(cls)
338+
self.set_container(cls)
339+
cls.__self__ = self
316340

317341
for provider in six.itervalues(cls.providers):
318342
_check_provider_type(cls, provider)
@@ -375,6 +399,28 @@ class DeclarativeContainerMetaClass(type):
375399
"""Return providers traversal generator."""
376400
yield from providers.traverse(*cls.providers.values(), types=types)
377401

402+
@staticmethod
403+
def __fetch_self(attributes):
404+
self = None
405+
alt_names = []
406+
407+
for name, value in attributes.items():
408+
if not isinstance(value, providers.Self):
409+
continue
410+
411+
if self is not None and value is not self:
412+
raise errors.Error('Container can have only one "Self" provider')
413+
414+
if name != '__self__':
415+
alt_names.append(name)
416+
417+
self = value
418+
419+
if self:
420+
self.set_alt_names(alt_names)
421+
422+
return self
423+
378424

379425
@six.add_metaclass(DeclarativeContainerMetaClass)
380426
class DeclarativeContainer(Container):
@@ -448,9 +494,21 @@ class DeclarativeContainer(Container):
448494
container = cls.instance_type()
449495
container.provider_type = cls.provider_type
450496
container.declarative_parent = cls
451-
container.set_providers(**providers.deepcopy(cls.providers))
497+
498+
copied_providers = providers.deepcopy({ **cls.providers, **{'@@self@@': cls.__self__}})
499+
copied_self = copied_providers.pop('@@self@@')
500+
copied_self.set_container(container)
501+
502+
container.__self__ = copied_self
503+
for name in copied_self.alt_names:
504+
container.set_provider(name, copied_self)
505+
506+
for name, provider in copied_providers.items():
507+
container.set_provider(name, provider)
508+
452509
container.override_providers(**overriding_providers)
453510
container.apply_container_providers_overridings()
511+
454512
return container
455513

456514
@classmethod

0 commit comments

Comments
 (0)