Repository: reagento/dishka
Pull request #132 (2024-04-17 22:25:56)

Provide all function


Patch:
diff --git a/docs/provider/alias.rst b/docs/provider/alias.rst
index 5dfe3a9..8f1777b 100644
--- a/docs/provider/alias.rst
+++ b/docs/provider/alias.rst
@@ -1,6 +1,6 @@
 .. _alias:
 
-@alias
+alias
 ****************
 
 ``alias`` is used to allow retrieving of the same object by different type hints. E.g. you have configured how to provide ``A`` object and want to use it as AProtocol: ``container.get(A)==container.get(AProtocol)``.
diff --git a/docs/provider/decorate.rst b/docs/provider/decorate.rst
index 858b003..dcde5f4 100644
--- a/docs/provider/decorate.rst
+++ b/docs/provider/decorate.rst
@@ -1,6 +1,6 @@
 .. _decorate:
 
-@decorate
+decorate
 *********************
 
 ``decorate`` is used to modify or wrap an object which is already configured in another ``Provider``.
diff --git a/docs/provider/from_context.rst b/docs/provider/from_context.rst
index 4f3612c..486b15c 100644
--- a/docs/provider/from_context.rst
+++ b/docs/provider/from_context.rst
@@ -1,6 +1,6 @@
 .. _from-context:
 
-@from_context
+from_context
 ****************
 
 You can put some data manually when entering scope and rely on it in your provider factories. To make it work you need to mark a dependency as retrieved from context using ``from_context`` and the use it as usual. Later, set ``context=`` argument when you enter corresponding scope.
diff --git a/docs/provider/index.rst b/docs/provider/index.rst
index 91d1463..472d472 100644
--- a/docs/provider/index.rst
+++ b/docs/provider/index.rst
@@ -100,6 +100,7 @@ Though it is a normal object, not all attributes are analyzed by ``Container``,
 .. toctree::
 
    provide
+   provide_all
    alias
    from_context
    decorate
diff --git a/docs/provider/provide.rst b/docs/provider/provide.rst
index d1d44f6..827a820 100644
--- a/docs/provider/provide.rst
+++ b/docs/provider/provide.rst
@@ -1,6 +1,6 @@
 .. _provide:
 
-@provide
+provide
 ******************
 
 ``provide`` function is used to declare a factory providing a dependency. It can be used with some class or as a method decorator (either sync or async). It supports finalization of dependency if you make it a generator.
diff --git a/docs/provider/provide_all.rst b/docs/provider/provide_all.rst
new file mode 100644
index 0000000..ce07443
--- /dev/null
+++ b/docs/provider/provide_all.rst
@@ -0,0 +1,31 @@
+.. _provide_all:
+
+provide_all
+******************
+
+``provide_all`` is a helper function which can be used instead of repeating ``provide`` call with just class passed and same scope
+
+These two providers are equal for the container
+
+.. code-block:: python
+
+    from dishka import provide, provide_all, Provider, Scope
+
+    class OneByOne(Provider):
+        scope = Scope.APP
+
+        a = provide(ClassA)
+        b = provide(ClassB)
+
+    class AllAtOnce(Provider):
+        scope = Scope.APP
+
+        ab = provide_all(ClassA, ClassB)
+
+
+It is also available as a method:
+
+.. code-block:: python
+
+    provider = Provider(scope=Scope.APP)
+    provider.provide_all(ClassA, ClassB)
\ No newline at end of file
diff --git a/src/dishka/__init__.py b/src/dishka/__init__.py
index 6e0229d..6d5f887 100644
--- a/src/dishka/__init__.py
+++ b/src/dishka/__init__.py
@@ -4,7 +4,8 @@ __all__ = [
     "DEFAULT_COMPONENT", "Component",
     "make_container", "Container", "FromComponent",
     "Provider",
-    "alias", "decorate", "from_context", "provide", "DependencyKey",
+    "alias", "decorate", "from_context", "provide", "provide_all",
+    "DependencyKey",
     "FromDishka",
     "BaseScope", "Scope", "new_scope",
 ]
@@ -16,6 +17,7 @@ from .dependency_source import (
     decorate,
     from_context,
     provide,
+    provide_all,
 )
 from .entities.component import DEFAULT_COMPONENT, Component
 from .entities.depends_marker import FromDishka
diff --git a/src/dishka/dependency_source/__init__.py b/src/dishka/dependency_source/__init__.py
index 020913c..7dad04c 100644
--- a/src/dishka/dependency_source/__init__.py
+++ b/src/dishka/dependency_source/__init__.py
@@ -3,7 +3,8 @@ __all__ = [
     "alias", "Alias",
     "context_var", "ContextVariable", "from_context",
     "decorate", "Decorator",
-    "provide", "Factory", "FactoryType",
+    "provide", "provide_all",
+    "Factory", "FactoryType",
 ]
 
 from .alias import Alias
@@ -14,4 +15,4 @@ from .factory import Factory, FactoryType
 from .make_alias import alias
 from .make_context_var import from_context
 from .make_decorator import decorate
-from .make_factory import provide
+from .make_factory import provide, provide_all
diff --git a/src/dishka/dependency_source/make_factory.py b/src/dishka/dependency_source/make_factory.py
index 9e1537b..6564de4 100644
--- a/src/dishka/dependency_source/make_factory.py
+++ b/src/dishka/dependency_source/make_factory.py
@@ -487,5 +487,41 @@ def provide(
     return scoped
 
 
-def _context_stub():
-    raise NotImplementedError
+def _provide_all(
+        *,
+        provides: Sequence[Any],
+        scope: BaseScope | None,
+        cache: bool,
+        is_in_class: bool,
+) -> CompositeDependencySource:
+    composite = CompositeDependencySource(None)
+    for single_provides in provides:
+        factory = make_factory(
+            provides=single_provides, scope=scope,
+            source=single_provides, cache=cache,
+            is_in_class=is_in_class,
+        )
+        composite.dependency_sources.extend(unpack_factory(factory))
+    return composite
+
+
+def provide_all(
+        *provides: Any,
+        scope: BaseScope | None = None,
+        cache: bool = True,
+) -> CompositeDependencySource:
+    return _provide_all(
+        provides=provides, scope=scope,
+        cache=cache, is_in_class=True,
+    )
+
+
+def provide_all_on_instance(
+        *provides: Any,
+        scope: BaseScope | None = None,
+        cache: bool = True,
+) -> CompositeDependencySource:
+    return _provide_all(
+        provides=provides, scope=scope,
+        cache=cache, is_in_class=False,
+    )
diff --git a/src/dishka/provider.py b/src/dishka/provider.py
index 79355ef..c3375b7 100644
--- a/src/dishka/provider.py
+++ b/src/dishka/provider.py
@@ -15,7 +15,10 @@ from .dependency_source import (
 )
 from .dependency_source.composite import CompositeDependencySource
 from .dependency_source.make_decorator import decorate_on_instance
-from .dependency_source.make_factory import provide_on_instance
+from .dependency_source.make_factory import (
+    provide_all_on_instance,
+    provide_on_instance,
+)
 
 
 def is_dependency_source(attribute: Any) -> bool:
@@ -141,6 +144,20 @@ class Provider(BaseProvider):
         self._add_dependency_sources(str(source), composite.dependency_sources)
         return composite
 
+    def provide_all(
+            self,
+            *provides: Any,
+            scope: BaseScope | None = None,
+            cache: bool = True,
+    ) -> CompositeDependencySource:
+        if scope is None:
+            scope = self.scope
+        composite = provide_all_on_instance(
+            *provides, scope=scope, cache=cache,
+        )
+        self._add_dependency_sources("?", composite.dependency_sources)
+        return composite
+
     def alias(
             self,
             *,
@@ -174,7 +191,7 @@ class Provider(BaseProvider):
 
     def from_context(
             self, *, provides: Any, scope: BaseScope,
-    ) -> ContextVariable:
+    ) -> CompositeDependencySource:
         composite = from_context(provides=provides, scope=scope)
         self._add_dependency_sources(str(provides),
                                      composite.dependency_sources)


Test patch:
diff --git a/tests/unit/test_provider.py b/tests/unit/test_provider.py
index d698670..f788642 100644
--- a/tests/unit/test_provider.py
+++ b/tests/unit/test_provider.py
@@ -4,7 +4,7 @@ import pytest
 
 from dishka import Provider, Scope, alias, provide
 from dishka.dependency_source import FactoryType
-from dishka.dependency_source.make_factory import make_factory
+from dishka.dependency_source.make_factory import make_factory, provide_all
 from dishka.entities.key import (
     hint_to_dependency_key,
     hints_to_dependency_keys,
@@ -247,3 +247,29 @@ def test_provide_protocol_impl():
     factory = impl.dependency_sources[0]
     assert factory.provides == hint_to_dependency_key(MyImpl)
     assert factory.dependencies == []
+
+
+class A:
+    pass
+
+
+class B:
+    pass
+
+
+def test_provide_all_cls():
+    class MyProvider(Provider):
+        x = provide_all(A, B)
+
+    provider = MyProvider(scope=Scope.APP)
+    assert len(provider.factories) == 2
+    provides = [f.provides.type_hint for f in provider.factories]
+    assert provides == [A, B]
+
+
+def test_provide_all_instance():
+    provider = Provider(scope=Scope.APP)
+    provider.provide_all(A, B)
+    assert len(provider.factories) == 2
+    provides = [f.provides.type_hint for f in provider.factories]
+    assert provides == [A, B]