Repository: reagento/dishka
Pull request #21 (2024-01-28 21:15:52)

Rename depedency provider variants


Patch:
diff --git a/src/dishka/__init__.py b/src/dishka/__init__.py
index e0fc3eb..70872a5 100644
--- a/src/dishka/__init__.py
+++ b/src/dishka/__init__.py
@@ -2,12 +2,14 @@ __all__ = [
     "make_async_container", "AsyncContainer",
     "make_container", "Container",
     "Depends", "wrap_injection",
-    "Provider", "provide", "alias",
+    "Provider",
+    "alias", "decorate", "provide",
     "BaseScope", "Scope",
 ]
 
 from .async_container import AsyncContainer, make_async_container
 from .container import Container, make_container
+from .dependency_source import alias, decorate, provide
 from .inject import Depends, wrap_injection
-from .provider import Provider, alias, provide
+from .provider import Provider
 from .scope import BaseScope, Scope
diff --git a/src/dishka/async_container.py b/src/dishka/async_container.py
index d33c4e5..8e49803 100644
--- a/src/dishka/async_container.py
+++ b/src/dishka/async_container.py
@@ -2,7 +2,8 @@ from asyncio import Lock
 from dataclasses import dataclass
 from typing import Callable, List, Optional, Type, TypeVar
 
-from .provider import DependencyProvider, Provider, ProviderType
+from .dependency_source import Factory, FactoryType
+from .provider import Provider
 from .registry import Registry, make_registries
 from .scope import BaseScope, Scope
 
@@ -12,7 +13,7 @@ T = TypeVar("T")
 @dataclass
 class Exit:
     __slots__ = ("type", "callable")
-    type: ProviderType
+    type: FactoryType
     callable: Callable
 
 
@@ -74,25 +75,25 @@ class AsyncContainer:
 
     async def _get_self(
             self,
-            dep_provider: DependencyProvider,
+            dep_provider: Factory,
     ) -> T:
         sub_dependencies = [
             await self._get_unlocked(dependency)
             for dependency in dep_provider.dependencies
         ]
-        if dep_provider.type is ProviderType.GENERATOR:
+        if dep_provider.type is FactoryType.GENERATOR:
             generator = dep_provider.source(*sub_dependencies)
             self.exits.append(Exit(dep_provider.type, generator))
             return next(generator)
-        elif dep_provider.type is ProviderType.ASYNC_GENERATOR:
+        elif dep_provider.type is FactoryType.ASYNC_GENERATOR:
             generator = dep_provider.source(*sub_dependencies)
             self.exits.append(Exit(dep_provider.type, generator))
             return await anext(generator)
-        elif dep_provider.type is ProviderType.ASYNC_FACTORY:
+        elif dep_provider.type is FactoryType.ASYNC_FACTORY:
             return await dep_provider.source(*sub_dependencies)
-        elif dep_provider.type is ProviderType.FACTORY:
+        elif dep_provider.type is FactoryType.FACTORY:
             return dep_provider.source(*sub_dependencies)
-        elif dep_provider.type is ProviderType.VALUE:
+        elif dep_provider.type is FactoryType.VALUE:
             return dep_provider.source
         else:
             raise ValueError(f"Unsupported type {dep_provider.type}")
@@ -120,9 +121,9 @@ class AsyncContainer:
         e = None
         for exit_generator in self.exits:
             try:
-                if exit_generator.type is ProviderType.ASYNC_GENERATOR:
+                if exit_generator.type is FactoryType.ASYNC_GENERATOR:
                     await anext(exit_generator.callable)
-                elif exit_generator.type is ProviderType.GENERATOR:
+                elif exit_generator.type is FactoryType.GENERATOR:
                     next(exit_generator.callable)
             except StopIteration:
                 pass
diff --git a/src/dishka/container.py b/src/dishka/container.py
index 2416251..75869ac 100644
--- a/src/dishka/container.py
+++ b/src/dishka/container.py
@@ -2,7 +2,8 @@ from dataclasses import dataclass
 from threading import Lock
 from typing import Callable, List, Optional, Type, TypeVar
 
-from .provider import DependencyProvider, Provider, ProviderType
+from .dependency_source import Factory, FactoryType
+from .provider import Provider
 from .registry import Registry, make_registries
 from .scope import BaseScope, Scope
 
@@ -12,7 +13,7 @@ T = TypeVar("T")
 @dataclass
 class Exit:
     __slots__ = ("type", "callable")
-    type: ProviderType
+    type: FactoryType
     callable: Callable
 
 
@@ -74,19 +75,19 @@ class Container:
 
     def _get_self(
             self,
-            dep_provider: DependencyProvider,
+            dep_provider: Factory,
     ) -> T:
         sub_dependencies = [
             self._get_unlocked(dependency)
             for dependency in dep_provider.dependencies
         ]
-        if dep_provider.type is ProviderType.GENERATOR:
+        if dep_provider.type is FactoryType.GENERATOR:
             generator = dep_provider.source(*sub_dependencies)
             self.exits.append(Exit(dep_provider.type, generator))
             return next(generator)
-        elif dep_provider.type is ProviderType.FACTORY:
+        elif dep_provider.type is FactoryType.FACTORY:
             return dep_provider.source(*sub_dependencies)
-        elif dep_provider.type is ProviderType.VALUE:
+        elif dep_provider.type is FactoryType.VALUE:
             return dep_provider.source
         else:
             raise ValueError(f"Unsupported type {dep_provider.type}")
@@ -114,7 +115,7 @@ class Container:
         e = None
         for exit_generator in self.exits:
             try:
-                if exit_generator.type is ProviderType.GENERATOR:
+                if exit_generator.type is FactoryType.GENERATOR:
                     next(exit_generator.callable)
             except StopIteration:
                 pass
@@ -126,6 +127,7 @@ class Container:
 
 class ContextWrapper:
     __slots__ = ("container",)
+
     def __init__(self, container: Container):
         self.container = container
 
diff --git a/src/dishka/dependency_source.py b/src/dishka/dependency_source.py
new file mode 100644
index 0000000..4b3b1f8
--- /dev/null
+++ b/src/dishka/dependency_source.py
@@ -0,0 +1,244 @@
+from collections.abc import AsyncIterable, Iterable
+from enum import Enum
+from inspect import (
+    isasyncgenfunction,
+    isclass,
+    iscoroutinefunction,
+    isgeneratorfunction,
+)
+from typing import (
+    Any,
+    Callable,
+    Optional,
+    Sequence,
+    Type,
+    Union,
+    get_args,
+    get_origin,
+    get_type_hints,
+    overload,
+)
+
+from .scope import BaseScope
+
+
+class FactoryType(Enum):
+    GENERATOR = "generator"
+    ASYNC_GENERATOR = "async_generator"
+    FACTORY = "factory"
+    ASYNC_FACTORY = "async_factory"
+    VALUE = "value"
+
+
+def _identity(x: Any) -> Any:
+    return x
+
+
+class Factory:
+    __slots__ = (
+        "dependencies", "source", "provides", "scope", "type",
+        "is_to_bound",
+    )
+
+    def __init__(
+            self,
+            dependencies: Sequence[Any],
+            source: Any,
+            provides: Type,
+            scope: Optional[BaseScope],
+            type: FactoryType,
+            is_to_bound: bool,
+    ):
+        self.dependencies = dependencies
+        self.source = source
+        self.provides = provides
+        self.scope = scope
+        self.type = type
+        self.is_to_bound = is_to_bound
+
+    def __get__(self, instance, owner):
+        if instance is None:
+            return self
+        if self.is_to_bound:
+            source = self.source.__get__(instance, owner)
+        else:
+            source = self.source
+        return Factory(
+            dependencies=self.dependencies,
+            source=source,
+            provides=self.provides,
+            scope=self.scope,
+            type=self.type,
+            is_to_bound=False,
+        )
+
+
+def make_factory(
+        provides: Any,
+        scope: Optional[BaseScope],
+        source: Callable,
+) -> Factory:
+    if isclass(source):
+        hints = get_type_hints(source.__init__, include_extras=True)
+        hints.pop("return", None)
+        possible_dependency = source
+        is_to_bind = False
+    else:
+        hints = get_type_hints(source, include_extras=True)
+        possible_dependency = hints.pop("return", None)
+        is_to_bind = True
+
+    if isclass(source):
+        provider_type = FactoryType.FACTORY
+    elif isasyncgenfunction(source):
+        provider_type = FactoryType.ASYNC_GENERATOR
+        if get_origin(possible_dependency) is AsyncIterable:
+            possible_dependency = get_args(possible_dependency)[0]
+        else:  # async generator
+            possible_dependency = get_args(possible_dependency)[0]
+    elif isgeneratorfunction(source):
+        provider_type = FactoryType.GENERATOR
+        if get_origin(possible_dependency) is Iterable:
+            possible_dependency = get_args(possible_dependency)[0]
+        else:  # generator
+            possible_dependency = get_args(possible_dependency)[1]
+    elif iscoroutinefunction(source):
+        provider_type = FactoryType.ASYNC_FACTORY
+    else:
+        provider_type = FactoryType.FACTORY
+
+    return Factory(
+        dependencies=list(hints.values()),
+        type=provider_type,
+        source=source,
+        scope=scope,
+        provides=provides or possible_dependency,
+        is_to_bound=is_to_bind,
+    )
+
+
+@overload
+def provide(
+        *,
+        scope: BaseScope,
+        provides: Any = None,
+) -> Callable[[Callable], Factory]:
+    ...
+
+
+@overload
+def provide(
+        source: Union[Callable, Type],
+        *,
+        scope: BaseScope,
+        provides: Any = None,
+) -> Factory:
+    ...
+
+
+def provide(
+        source: Union[None, Callable, Type] = None,
+        *,
+        scope: BaseScope,
+        provides: Any = None,
+):
+    """
+    Mark a method or class as providing some dependency.
+
+    If used as a method decorator then return annotation is used
+    to determine what is provided. User `provides` to override that.
+    Method parameters are analyzed and passed automatically.
+
+    If used with a class a first parameter than `__init__` method parameters
+    are passed automatically. If no provides is passed then it is
+    supposed that class itself is a provided dependency.
+
+    Return value must be saved as a `Provider` class attribute and
+    not intended for direct usage
+
+    :param source: Method to decorate or class.
+    :param scope: Scope of the dependency to limit its lifetime
+    :param provides: Dependency type which is provided by this factory
+    :return: instance of Factory or a decorator returning it
+    """
+    if source is not None:
+        return make_factory(provides, scope, source)
+
+    def scoped(func):
+        return make_factory(provides, scope, func)
+
+    return scoped
+
+
+class Alias:
+    __slots__ = ("source", "provides")
+
+    def __init__(self, source, provides):
+        self.source = source
+        self.provides = provides
+
+    def as_provider(self, scope: BaseScope) -> Factory:
+        return Factory(
+            scope=scope,
+            source=_identity,
+            provides=self.provides,
+            is_to_bound=False,
+            dependencies=[self.source],
+            type=FactoryType.FACTORY,
+        )
+
+    def __get__(self, instance, owner):
+        return self
+
+
+def alias(
+        *,
+        source: Type,
+        provides: Type,
+):
+    return Alias(
+        source=source,
+        provides=provides,
+    )
+
+
+class Decorator:
+    __slots__ = ("provides", "provider")
+
+    def __init__(self, provider: Factory):
+        self.provider = provider
+        self.provides = provider.provides
+
+    def as_provider(
+            self, scope: BaseScope, new_dependency: Any,
+    ) -> Factory:
+        return Factory(
+            scope=scope,
+            source=self.provider.source,
+            provides=self.provider.provides,
+            is_to_bound=self.provider.is_to_bound,
+            dependencies=[
+                new_dependency if dep is self.provides else dep
+                for dep in self.provider.dependencies
+            ],
+            type=self.provider.type,
+        )
+
+    def __get__(self, instance, owner):
+        return Decorator(self.provider.__get__(instance, owner))
+
+
+def decorate(
+        source: Union[None, Callable, Type] = None,
+        provides: Any = None,
+):
+    if source is not None:
+        return Decorator(make_factory(provides, None, source))
+
+    def scoped(func):
+        return Decorator(make_factory(provides, None, func))
+
+    return scoped
+
+
+DependencySource = Alias | Factory | Decorator
diff --git a/src/dishka/provider.py b/src/dishka/provider.py
index d9364ab..372a380 100644
--- a/src/dishka/provider.py
+++ b/src/dishka/provider.py
@@ -1,252 +1,11 @@
-from collections.abc import AsyncIterable, Iterable
-from enum import Enum
-from inspect import (
-    isasyncgenfunction,
-    isclass,
-    iscoroutinefunction,
-    isgeneratorfunction,
-)
-from typing import (
-    Any,
-    Callable,
-    List,
-    Optional,
-    Sequence,
-    Type,
-    Union,
-    get_args,
-    get_origin,
-    get_type_hints, overload,
-)
+from typing import List
 
-from .scope import BaseScope
-
-
-class ProviderType(Enum):
-    GENERATOR = "generator"
-    ASYNC_GENERATOR = "async_generator"
-    FACTORY = "factory"
-    ASYNC_FACTORY = "async_factory"
-    VALUE = "value"
-
-
-def _identity(x: Any) -> Any:
-    return x
-
-
-class DependencyProvider:
-    __slots__ = (
-        "dependencies", "source", "provides", "scope", "type",
-        "is_to_bound",
-    )
-
-    def __init__(
-            self,
-            dependencies: Sequence[Any],
-            source: Any,
-            provides: Type,
-            scope: Optional[BaseScope],
-            type: ProviderType,
-            is_to_bound: bool,
-    ):
-        self.dependencies = dependencies
-        self.source = source
-        self.provides = provides
-        self.scope = scope
-        self.type = type
-        self.is_to_bound = is_to_bound
-
-    def __get__(self, instance, owner):
-        if instance is None:
-            return self
-        if self.is_to_bound:
-            source = self.source.__get__(instance, owner)
-        else:
-            source = self.source
-        return DependencyProvider(
-            dependencies=self.dependencies,
-            source=source,
-            provides=self.provides,
-            scope=self.scope,
-            type=self.type,
-            is_to_bound=False,
-        )
-
-
-def make_dependency_provider(
-        provides: Any,
-        scope: Optional[BaseScope],
-        source: Callable,
-):
-    if isclass(source):
-        hints = get_type_hints(source.__init__, include_extras=True)
-        hints.pop("return", None)
-        possible_dependency = source
-        is_to_bind = False
-    else:
-        hints = get_type_hints(source, include_extras=True)
-        possible_dependency = hints.pop("return", None)
-        is_to_bind = True
-
-    if isclass(source):
-        provider_type = ProviderType.FACTORY
-    elif isasyncgenfunction(source):
-        provider_type = ProviderType.ASYNC_GENERATOR
-        if get_origin(possible_dependency) is AsyncIterable:
-            possible_dependency = get_args(possible_dependency)[0]
-        else:  # async generator
-            possible_dependency = get_args(possible_dependency)[0]
-    elif isgeneratorfunction(source):
-        provider_type = ProviderType.GENERATOR
-        if get_origin(possible_dependency) is Iterable:
-            possible_dependency = get_args(possible_dependency)[0]
-        else:  # generator
-            possible_dependency = get_args(possible_dependency)[1]
-    elif iscoroutinefunction(source):
-        provider_type = ProviderType.ASYNC_FACTORY
-    else:
-        provider_type = ProviderType.FACTORY
-
-    return DependencyProvider(
-        dependencies=list(hints.values()),
-        type=provider_type,
-        source=source,
-        scope=scope,
-        provides=provides or possible_dependency,
-        is_to_bound=is_to_bind,
-    )
-
-
-class Alias:
-    __slots__ = ("source", "provides")
-
-    def __init__(self, source, provides):
-        self.source = source
-        self.provides = provides
-
-    def as_provider(self, scope: BaseScope) -> DependencyProvider:
-        return DependencyProvider(
-            scope=scope,
-            source=_identity,
-            provides=self.provides,
-            is_to_bound=False,
-            dependencies=[self.source],
-            type=ProviderType.FACTORY,
-        )
-
-    def __get__(self, instance, owner):
-        return self
-
-
-def alias(
-        *,
-        source: Type,
-        provides: Type,
-):
-    return Alias(
-        source=source,
-        provides=provides,
-    )
-
-
-class Decorator:
-    __slots__ = ("provides", "provider")
-
-    def __init__(self, provider: DependencyProvider):
-        self.provider = provider
-        self.provides = provider.provides
-
-    def as_provider(
-            self, scope: BaseScope, new_dependency: Any,
-    ) -> DependencyProvider:
-        return DependencyProvider(
-            scope=scope,
-            source=self.provider.source,
-            provides=self.provider.provides,
-            is_to_bound=self.provider.is_to_bound,
-            dependencies=[
-                new_dependency if dep is self.provides else dep
-                for dep in self.provider.dependencies
-            ],
-            type=self.provider.type,
-        )
-
-    def __get__(self, instance, owner):
-        return Decorator(self.provider.__get__(instance, owner))
-
-
-def decorate(
-        source: Union[None, Callable, Type] = None,
-        provides: Any = None,
-):
-    if source is not None:
-        return Decorator(make_dependency_provider(provides, None, source))
-
-    def scoped(func):
-        return Decorator(make_dependency_provider(provides, None, func))
-
-    return scoped
-
-
-@overload
-def provide(
-        *,
-        scope: BaseScope,
-        provides: Any = None,
-) -> Callable[[Callable], DependencyProvider]:
-    ...
-
-
-@overload
-def provide(
-        source: Union[Callable, Type],
-        *,
-        scope: BaseScope,
-        provides: Any = None,
-) -> DependencyProvider:
-    ...
-
-
-def provide(
-        source: Union[None, Callable, Type] = None,
-        *,
-        scope: BaseScope,
-        provides: Any = None,
-):
-    """
-    Mark a method or class as providing some dependency.
-
-    If used as a method decorator then return annotation is used
-    to determine what is provided. User `provides` to override that.
-    Method parameters are analyzed and passed automatically.
-
-    If used with a class a first parameter than `__init__` method parameters
-    are passed automatically. If no provides is passed then it is
-    supposed that class itself is a provided dependency.
-
-    Return value must be saved as a `Provider` class attribute and
-    not intended for direct usage
-
-    :param source: Method to decorate or class.
-    :param scope: Scope of the dependency to limit its lifetime
-    :param provides: Dependency type which is provided by this factory
-    :return: instance of DependencyProvider or a decorator returning it
-    """
-    if source is not None:
-        return make_dependency_provider(provides, scope, source)
-
-    def scoped(func):
-        return make_dependency_provider(provides, scope, func)
-
-    return scoped
-
-
-DependencyProviderVariant = Alias | DependencyProvider | Decorator
+from .dependency_source import DependencySource
 
 
 class Provider:
     """
-    A collection of factories providing dependencies.
+    A collection of dependency sources.
 
     Inherit this class and add attributes using
     `provide`, `alias` or `decorate`.
@@ -259,7 +18,8 @@ class Provider:
     """
 
     def __init__(self):
-        self.dependency_providers: List[DependencyProviderVariant] = []
-        for name, attr in vars(type(self)).items():
-            if isinstance(attr, DependencyProviderVariant):
-                self.dependency_providers.append(getattr(self, name))
+        self.dependency_sources: List[DependencySource] = [
+            getattr(self, name)
+            for name, attr in vars(type(self)).items()
+            if isinstance(attr, DependencySource)
+        ]
diff --git a/src/dishka/registry.py b/src/dishka/registry.py
index 0565738..6dd569d 100644
--- a/src/dishka/registry.py
+++ b/src/dishka/registry.py
@@ -1,6 +1,7 @@
 from typing import Any, List, NewType, Type
 
-from .provider import Alias, Decorator, DependencyProvider, Provider
+from .dependency_source import Alias, Decorator, Factory
+from .provider import Provider
 from .scope import BaseScope
 
 
@@ -11,10 +12,10 @@ class Registry:
         self._providers = {}
         self.scope = scope
 
-    def add_provider(self, provider: DependencyProvider):
+    def add_provider(self, provider: Factory):
         self._providers[provider.provides] = provider
 
-    def get_provider(self, dependency: Any) -> DependencyProvider:
+    def get_provider(self, dependency: Any) -> Factory:
         return self._providers.get(dependency)
 
 
@@ -23,35 +24,35 @@ def make_registries(
 ) -> List[Registry]:
     dep_scopes = {}
     for provider in providers:
-        for dep_provider in provider.dependency_providers:
-            if hasattr(dep_provider, "scope"):
-                dep_scopes[dep_provider.provides] = dep_provider.scope
+        for source in provider.dependency_sources:
+            if hasattr(source, "scope"):
+                dep_scopes[source.provides] = source.scope
 
     registries = {scope: Registry(scope) for scope in scopes}
 
     for provider in providers:
-        for dep_provider in provider.dependency_providers:
-            if isinstance(dep_provider, DependencyProvider):
-                scope = dep_provider.scope
-            elif isinstance(dep_provider, Alias):
-                scope = dep_scopes[dep_provider.source]
-                dep_scopes[dep_provider.provides] = scope
-                dep_provider = dep_provider.as_provider(scope)
-            elif isinstance(dep_provider, Decorator):
-                scope = dep_scopes[dep_provider.provides]
+        for source in provider.dependency_sources:
+            if isinstance(source, Factory):
+                scope = source.scope
+            elif isinstance(source, Alias):
+                scope = dep_scopes[source.source]
+                dep_scopes[source.provides] = scope
+                source = source.as_provider(scope)
+            elif isinstance(source, Decorator):
+                scope = dep_scopes[source.provides]
                 registry = registries[scope]
                 undecorated_type = NewType(
-                    f"Old_{dep_provider.provides.__name__}",
-                    dep_provider.provides,
+                    f"Old_{source.provides.__name__}",
+                    source.provides,
                 )
-                old_provider = registry.get_provider(dep_provider.provides)
+                old_provider = registry.get_provider(source.provides)
                 old_provider.provides = undecorated_type
                 registry.add_provider(old_provider)
-                dep_provider = dep_provider.as_provider(
+                source = source.as_provider(
                     scope, undecorated_type,
                 )
             else:
-                raise
-            registries[scope].add_provider(dep_provider)
+                raise ValueError("Unknown dependency source type")
+            registries[scope].add_provider(source)
 
     return list(registries.values())


Test patch:
diff --git a/tests/container/test_decorator.py b/tests/container/test_decorator.py
index 55eaa5e..dd8133d 100644
--- a/tests/container/test_decorator.py
+++ b/tests/container/test_decorator.py
@@ -1,5 +1,4 @@
-from dishka import Provider, Scope, make_container, provide
-from dishka.provider import alias, decorate
+from dishka import Provider, Scope, alias, decorate, make_container, provide
 
 
 class A:
diff --git a/tests/test_provider.py b/tests/test_provider.py
index c3336a4..098bb44 100644
--- a/tests/test_provider.py
+++ b/tests/test_provider.py
@@ -1,7 +1,7 @@
 import pytest
 
 from dishka import Provider, Scope, alias, provide
-from dishka.provider import ProviderType
+from dishka.dependency_source import FactoryType
 from .sample_providers import (
     ClassA,
     async_func_a,
@@ -23,25 +23,25 @@ def test_provider_init():
             return f"{x}"
 
     provider = MyProvider()
-    assert len(provider.dependency_providers) == 3
+    assert len(provider.dependency_sources) == 3
 
 
 @pytest.mark.parametrize(
-    "factory, provider_type, is_to_bound", [
-        (ClassA, ProviderType.FACTORY, False),
-        (sync_func_a, ProviderType.FACTORY, True),
-        (sync_iter_a, ProviderType.GENERATOR, True),
-        (sync_gen_a, ProviderType.GENERATOR, True),
-        (async_func_a, ProviderType.ASYNC_FACTORY, True),
-        (async_iter_a, ProviderType.ASYNC_GENERATOR, True),
-        (async_gen_a, ProviderType.ASYNC_GENERATOR, True),
+    "source, provider_type, is_to_bound", [
+        (ClassA, FactoryType.FACTORY, False),
+        (sync_func_a, FactoryType.FACTORY, True),
+        (sync_iter_a, FactoryType.GENERATOR, True),
+        (sync_gen_a, FactoryType.GENERATOR, True),
+        (async_func_a, FactoryType.ASYNC_FACTORY, True),
+        (async_iter_a, FactoryType.ASYNC_GENERATOR, True),
+        (async_gen_a, FactoryType.ASYNC_GENERATOR, True),
     ],
 )
-def test_parse_provider(factory, provider_type, is_to_bound):
-    dep_provider = provide(factory, scope=Scope.REQUEST)
-    assert dep_provider.provides == ClassA
-    assert dep_provider.dependencies == [int]
-    assert dep_provider.is_to_bound == is_to_bound
-    assert dep_provider.scope == Scope.REQUEST
-    assert dep_provider.source == factory
-    assert dep_provider.type == provider_type
+def test_parse_factory(source, provider_type, is_to_bound):
+    factory = provide(source, scope=Scope.REQUEST)
+    assert factory.provides == ClassA
+    assert factory.dependencies == [int]
+    assert factory.is_to_bound == is_to_bound
+    assert factory.scope == Scope.REQUEST
+    assert factory.source == source
+    assert factory.type == provider_type