Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/dishka/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
__all__ = [
"DEFAULT_COMPONENT",
"STRICT_VALIDATION",
"ActivationContext",
"Activator",
"AnyOf",
"AsyncContainer",
"BaseScope",
Expand All @@ -9,6 +11,7 @@
"DependencyKey",
"FromComponent",
"FromDishka",
"Has",
"Provider",
"Scope",
"ValidationSettings",
Expand All @@ -25,6 +28,7 @@

from .async_container import AsyncContainer, make_async_container
from .container import Container, make_container
from .entities.activator import ActivationContext, Activator, Has
from .entities.component import DEFAULT_COMPONENT, Component
from .entities.depends_marker import FromDishka
from .entities.key import DependencyKey, FromComponent
Expand Down
1 change: 1 addition & 0 deletions src/dishka/async_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ def make_async_container(
providers=(*providers, context_provider),
skip_validation=skip_validation,
validation_settings=validation_settings,
root_context=context,
).build()
container = AsyncContainer(
*registries,
Expand Down
1 change: 1 addition & 0 deletions src/dishka/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ def make_container(
providers=(*providers, context_provider),
skip_validation=skip_validation,
validation_settings=validation_settings,
root_context=context,
).build()
container = Container(
*registries,
Expand Down
9 changes: 8 additions & 1 deletion src/dishka/dependency_source/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any

from dishka.entities.activator import Activator
from dishka.entities.component import Component
from dishka.entities.factory_type import FactoryType
from dishka.entities.key import DependencyKey
Expand All @@ -14,19 +15,24 @@ def _identity(x: Any) -> Any:


class Alias:
__slots__ = ("cache", "component", "override", "provides", "source")
__slots__ = (
"cache", "component", "override",
"provides", "source", "when",
)

def __init__(
self, *,
source: DependencyKey,
provides: DependencyKey,
cache: bool,
override: bool,
when: Activator | None,
) -> None:
self.source = source
self.provides = provides
self.cache = cache
self.override = override
self.when = when

def as_factory(
self, scope: BaseScope | None, component: Component | None,
Expand All @@ -41,6 +47,7 @@ def as_factory(
type_=FactoryType.ALIAS,
cache=self.cache,
override=self.override,
when=self.when,
)

def __get__(self, instance: Any, owner: Any) -> Alias:
Expand Down
12 changes: 8 additions & 4 deletions src/dishka/dependency_source/context_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any, NoReturn

from dishka.entities.activator import Activator
from dishka.entities.component import DEFAULT_COMPONENT, Component
from dishka.entities.factory_type import FactoryType
from dishka.entities.key import DependencyKey
Expand All @@ -15,21 +16,21 @@ def context_stub() -> NoReturn:


class ContextVariable:
__slots__ = ("override", "provides", "scope")
__slots__ = ("override", "provides", "scope", "when")

def __init__(
self, *,
provides: DependencyKey,
scope: BaseScope | None,
override: bool,
when: Activator | None,
) -> None:
self.provides = provides
self.scope = scope
self.override = override
self.when = when

def as_factory(
self, component: Component,
) -> Factory:
def as_factory(self, component: Component | None) -> Factory:
if component == DEFAULT_COMPONENT:
return Factory(
scope=self.scope,
Expand All @@ -41,6 +42,7 @@ def as_factory(
type_=FactoryType.CONTEXT,
cache=False,
override=self.override,
when=self.when,
)
else:
aliased = Alias(
Expand All @@ -51,6 +53,7 @@ def as_factory(
component=component,
type_hint=self.provides.type_hint,
),
when=self.when,
)
return aliased.as_factory(scope=self.scope, component=component)

Expand All @@ -60,4 +63,5 @@ def __get__(self, instance: Any, owner: Any) -> ContextVariable:
scope=scope,
provides=self.provides,
override=self.override,
when=self.when,
)
9 changes: 7 additions & 2 deletions src/dishka/dependency_source/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any, TypeVar, get_args, get_origin

from dishka.entities.activator import Activator
from dishka.entities.component import Component
from dishka.entities.key import DependencyKey
from dishka.entities.scope import BaseScope
Expand All @@ -10,20 +11,22 @@


class Decorator:
__slots__ = ("factory", "provides", "scope")
__slots__ = ("factory", "provides", "scope", "when")

def __init__(
self,
factory: Factory,
provides: DependencyKey | None = None,
scope: BaseScope | None = None,
when: Activator | None = None,
) -> None:
self.factory = factory
if provides:
self.provides = provides
else:
self.provides = factory.provides
self.scope = scope
self.when = when

def is_generic(self) -> bool:
return (
Expand All @@ -39,7 +42,7 @@ def as_factory(
scope: BaseScope,
new_dependency: DependencyKey,
cache: bool,
component: Component,
component: Component | None,
) -> Factory:
typevar_replacement = get_typevar_replacement(
self.provides.type_hint,
Expand Down Expand Up @@ -67,6 +70,7 @@ def as_factory(
type_=self.factory.type,
cache=cache,
override=False,
when=self.when,
)

def _replace_dep(
Expand Down Expand Up @@ -95,4 +99,5 @@ def __get__(self, instance: Any, owner: Any) -> Decorator:
return Decorator(
self.factory.__get__(instance, owner),
scope=self.scope,
when=self.when,
)
8 changes: 7 additions & 1 deletion src/dishka/dependency_source/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
)
from typing import Any

from dishka.entities.activator import Activator
from dishka.entities.component import Component
from dishka.entities.factory_type import FactoryData, FactoryType
from dishka.entities.key import DependencyKey
Expand All @@ -19,6 +20,7 @@ class Factory(FactoryData):
"is_to_bind",
"kw_dependencies",
"override",
"when",
)

def __init__(
Expand All @@ -33,6 +35,7 @@ def __init__(
is_to_bind: bool,
cache: bool,
override: bool,
when: Activator | None,
) -> None:
super().__init__(
source=source,
Expand All @@ -45,6 +48,7 @@ def __init__(
self.is_to_bind = is_to_bind
self.cache = cache
self.override = override
self.when = when

def __get__(self, instance: Any, owner: Any) -> Factory:
scope = self.scope or instance.scope
Expand All @@ -66,9 +70,10 @@ def __get__(self, instance: Any, owner: Any) -> Factory:
is_to_bind=False,
cache=self.cache,
override=self.override,
when=self.when,
)

def with_component(self, component: Component) -> Factory:
def with_component(self, component: Component | None) -> Factory:
return Factory(
dependencies=[
d.with_component(component) for d in self.dependencies
Expand All @@ -84,4 +89,5 @@ def with_component(self, component: Component) -> Factory:
cache=self.cache,
type_=self.type,
override=self.override,
when=self.when,
)
43 changes: 43 additions & 0 deletions src/dishka/entities/activator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from abc import abstractmethod
from collections.abc import Callable
from typing import Any, NamedTuple, Protocol, TypeAlias

from dishka.entities.component import Component
from dishka.entities.key import DependencyKey


class ActivationBuilder(Protocol):
@abstractmethod
def has_active(
self,
key: DependencyKey,
request_stack: list[DependencyKey],
) -> bool:
raise NotImplementedError


class ActivationContext(NamedTuple):
container_context: dict[Any, Any] | None
container_key: DependencyKey
key: DependencyKey
builder: ActivationBuilder
request_stack: list[DependencyKey]


Activator: TypeAlias = Callable[[ActivationContext], bool]


class Has:
def __init__(
self,
cls: Any,
*,
component: Component | None = None,
) -> None:
self.key = DependencyKey(cls, component=component)

def __call__(self, ctx: ActivationContext) -> bool:
key = self.key.with_component(ctx.key.component)
if key in ctx.request_stack: # cycle
return True
return ctx.builder.has_active(key, ctx.request_stack)
21 changes: 18 additions & 3 deletions src/dishka/provider/base_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,37 @@
Decorator,
Factory,
)
from dishka.entities.activator import Activator
from dishka.entities.component import Component


class BaseProvider:
def __init__(self, component: Component | None) -> None:
when: Activator | None = None
component: Component | None = None

def __init__(
self,
component: Component | None,
when: Activator | None = None,
) -> None:
if component is not None:
self.component = component
if when is not None:
self.when = when
self.factories: list[Factory] = []
self.aliases: list[Alias] = []
self.decorators: list[Decorator] = []
self.context_vars: list[ContextVariable] = []


class ProviderWrapper(BaseProvider):
def __init__(self, component: Component, provider: BaseProvider) -> None:
super().__init__(component)
def __init__(
self,
component: Component,
provider: BaseProvider,
when: Activator | None = None,
) -> None:
super().__init__(component, when=when)
self.factories.extend(provider.factories)
self.aliases.extend(provider.aliases)
self.decorators.extend(provider.decorators)
3 changes: 3 additions & 0 deletions src/dishka/provider/make_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
CompositeDependencySource,
ensure_composite,
)
from dishka.entities.activator import Activator
from dishka.entities.component import Component
from dishka.entities.key import hint_to_dependency_key
from .unpack_provides import unpack_alias
Expand All @@ -17,6 +18,7 @@ def alias(
cache: bool = True,
component: Component | None = None,
override: bool = False,
when: Activator | None = None,
) -> CompositeDependencySource:
if component is provides is None:
raise ValueError( # noqa: TRY003
Expand All @@ -31,6 +33,7 @@ def alias(
provides=hint_to_dependency_key(provides),
cache=cache,
override=override,
when=when,
)
composite.dependency_sources.extend(unpack_alias(alias_instance))
return composite
4 changes: 4 additions & 0 deletions src/dishka/provider/make_context_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
ContextVariable,
context_stub,
)
from dishka.entities.activator import Activator
from dishka.entities.component import DEFAULT_COMPONENT
from dishka.entities.key import DependencyKey
from dishka.entities.scope import BaseScope
Expand All @@ -20,6 +21,7 @@ def from_context(
*,
scope: BaseScope | None = None,
override: bool = False,
when: Activator | None = None,
) -> CompositeDependencySource:
composite = CompositeDependencySource(origin=context_stub)
composite.dependency_sources.append(
Expand All @@ -30,6 +32,7 @@ def from_context(
type_hint=provides,
component=DEFAULT_COMPONENT,
),
when=when,
),
)

Expand All @@ -41,6 +44,7 @@ def from_context(
provides=DependencyKey(base_type, DEFAULT_COMPONENT),
cache=True,
override=override,
when=when,
),
)
return composite
Loading
Loading