Repository: reagento/dishka
Pull request #144 (2024-04-26 21:43:15)

v1.1.1


Patch:
diff --git a/pyproject.toml b/pyproject.toml
index 618919e..9a06ec2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,7 +10,7 @@ where = ["src"]
 
 [project]
 name = "dishka"
-version = "1.1.0"
+version = "0.1"
 readme = "README.md"
 authors = [
     { name = "Andrey Tikhonov", email = "17@itishka.org" },
diff --git a/src/dishka/dependency_source/make_factory.py b/src/dishka/dependency_source/make_factory.py
index 91efb63..f9db170 100644
--- a/src/dishka/dependency_source/make_factory.py
+++ b/src/dishka/dependency_source/make_factory.py
@@ -10,6 +10,7 @@ from collections.abc import (
 )
 from inspect import (
     isasyncgenfunction,
+    isbuiltin,
     isclass,
     iscoroutinefunction,
     isfunction,
@@ -226,7 +227,7 @@ def _make_factory_by_class(
     )
 
 
-def _make_factory_by_method(
+def _make_factory_by_function(
         *,
         provides: Any,
         scope: BaseScope | None,
@@ -387,10 +388,15 @@ def make_factory(
             provides=provides, scope=scope, source=source, cache=cache,
         )
     elif isfunction(source) or isinstance(source, classmethod):
-        return _make_factory_by_method(
+        return _make_factory_by_function(
             provides=provides, scope=scope, source=source, cache=cache,
             is_in_class=is_in_class,
         )
+    elif isbuiltin(source):
+        return _make_factory_by_function(
+            provides=provides, scope=scope, source=source, cache=cache,
+            is_in_class=False,
+        )
     elif isinstance(source, staticmethod):
         return _make_factory_by_static_method(
             provides=provides, scope=scope, source=source, cache=cache,
diff --git a/src/dishka/registry.py b/src/dishka/registry.py
index a072052..f273943 100644
--- a/src/dishka/registry.py
+++ b/src/dishka/registry.py
@@ -88,8 +88,13 @@ class Registry:
             self, factory: Factory, dependency_key: DependencyKey,
     ) -> Factory:
         dependency = dependency_key.type_hint
+        type_var_deps = (
+            d.type_hint
+            for d in factory.dependencies
+            if isinstance(d.type_hint, TypeVar)
+        )
         params_replacement = dict(zip(
-            get_args(factory.provides.type_hint),
+            type_var_deps,
             get_args(dependency),
             strict=False,
         ))
@@ -158,7 +163,8 @@ class GraphValidator:
 
     def validate(self):
         for registry_index, registry in enumerate(self.registries):
-            for factory in registry.factories.values():
+            factories = tuple(registry.factories.values())
+            for factory in factories:
                 self.path = {}
                 try:
                     self._validate_factory(factory, registry_index)


Test patch:
diff --git a/tests/unit/container/test_generic.py b/tests/unit/container/test_generic.py
index 054c06c..65b4910 100644
--- a/tests/unit/container/test_generic.py
+++ b/tests/unit/container/test_generic.py
@@ -3,6 +3,7 @@ from typing import Generic, TypeVar
 import pytest
 
 from dishka import Provider, Scope, make_container, provide
+from dishka.exceptions import GraphMissingFactoryError
 
 T = TypeVar("T")
 U = TypeVar("U")
@@ -113,3 +114,51 @@ def test_generic_func():
     a = container.get(A[int])
     assert isinstance(a, A)
     assert a.x == 42
+
+
+class Event:
+    pass
+
+
+EventsT = TypeVar("EventsT")
+
+
+class EventEmitter(Generic[EventsT]):
+    pass
+
+
+class EventEmitterImpl(EventEmitter[EventsT]):
+    def __init__(self, x: EventsT) -> None:
+        pass
+
+
+def factory(event_emitter: EventEmitter[Event]) -> int:
+    return 1
+
+
+def factory_invalid(event_emitter: EventEmitter[str]) -> int:
+    return 1
+
+
+def test_generic_validation_ok():
+    provider = Provider(scope=Scope.APP)
+    provider.provide(EventEmitterImpl, provides=EventEmitter)
+    provider.provide(source=lambda: None, provides=Event)
+    provider.provide(factory)
+    assert make_container(provider)
+
+
+def test_generic_validation_typevar_ok():
+    provider = Provider(scope=Scope.APP)
+    provider.provide(EventEmitterImpl[EventsT], provides=EventEmitter[EventsT])
+    provider.provide(source=lambda: None, provides=Event)
+    provider.provide(factory)
+    assert make_container(provider)
+
+
+def test_generic_validation_fail():
+    provider = Provider(scope=Scope.APP)
+    provider.provide(EventEmitterImpl, provides=EventEmitter)
+    provider.provide(factory_invalid)
+    with pytest.raises(GraphMissingFactoryError):
+        assert make_container(provider)
diff --git a/tests/unit/test_provider.py b/tests/unit/test_provider.py
index f788642..acf7ecb 100644
--- a/tests/unit/test_provider.py
+++ b/tests/unit/test_provider.py
@@ -1,3 +1,4 @@
+from random import random
 from typing import Any, Protocol
 
 import pytest
@@ -273,3 +274,9 @@ def test_provide_all_instance():
     assert len(provider.factories) == 2
     provides = [f.provides.type_hint for f in provider.factories]
     assert provides == [A, B]
+
+
+def test_privde_random():
+    source = provide(source=random, provides=float)
+    assert len(source.dependency_sources) == 1
+    assert not source.dependency_sources[0].dependencies