Repository: reagento/dishka

Issue:
Graph validation fails with depends on Generic[T]
## MRE

```python
from abc import ABC

from dishka import make_container, Provider, Scope
from typing import Generic, TypeVar


class Event(ABC):
    ...


EventsT = TypeVar("EventsT")


class EventEmitter(Generic[EventsT], ABC):
    ...


class EventEmitterImpl(EventEmitter[EventsT]):
    ...


def factory(event_emitter: EventEmitter[Event]) -> int:
    return 1


provider = Provider()

provider.provide(EventEmitterImpl, scope=Scope.REQUEST, provides=EventEmitter)
provider.provide(factory, scope=Scope.REQUEST)

container = make_container(provider)

```

## Traceback

```sh
Traceback (most recent call last):
  File "/home/lubaskincode/.config/JetBrains/PyCharm2023.3/scratches/test.py", line 32, in <module>
    container = make_container(provider)
  File "/home/lubaskincode/prj/PycharmProjects/zametka-api/.venv/lib/python3.10/site-packages/dishka/container.py", line 172, in make_container
    ).build()
  File "/home/lubaskincode/prj/PycharmProjects/zametka-api/.venv/lib/python3.10/site-packages/dishka/registry.py", line 340, in build
    GraphValidator(registries).validate()
  File "/home/lubaskincode/prj/PycharmProjects/zametka-api/.venv/lib/python3.10/site-packages/dishka/registry.py", line 161, in validate
    for factory in registry.factories.values():
RuntimeError: dictionary changed size during iteration
```

## skip_validation=True as temporary fix

Discussion:

Patch:
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)