Source code for diwire._internal.scope
from __future__ import annotations
from collections.abc import Iterator
from dataclasses import dataclass, field
from typing import Any
[docs]
class BaseScope(int):
"""Represent a numeric DI scope level with transition metadata.
A scope behaves like an ``int`` so comparisons and ordering are based on
``level``. ``skippable`` marks helper scopes that can be skipped by default
transitions when ``enter_scope(None)`` chooses the next non-skippable scope.
"""
def __new__(cls, *args: Any, **_kwargs: Any) -> BaseScope: # noqa: PYI034
return super().__new__(cls, *args)
def __init__(self, level: int, *, skippable: bool = False) -> None:
self.skippable = skippable
self.level = level
def __set_name__(
self,
owner: type[BaseScopes],
name: str,
) -> None:
self.owner = owner
self.scope_name = name
def __repr__(self) -> str:
return f"Scope.{self.scope_name}({self.level}, skippable={self.skippable})"
@dataclass(frozen=True, kw_only=True)
class BaseScopes:
"""Collect scope constants and derive helper subsets.
The ``skippable`` tuple is populated automatically from declared scope
members and is used by resolver transition logic.
"""
skippable: tuple[BaseScope, ...] = field(init=False)
def __post_init__(self) -> None:
object.__setattr__(
self,
"skippable",
tuple(scope for scope in self if scope.skippable),
)
def __iter__(self) -> Iterator[BaseScope]:
for value in self.__dict__.values():
if isinstance(value, BaseScope):
yield value
@dataclass(frozen=True)
class Scopes(BaseScopes):
"""Define the built-in DI scope ladder ordered by ``level``.
Levels increase with nesting depth. Calling ``enter_scope()`` with no
explicit target transitions to the next deeper non-skippable scope.
Examples:
.. code-block:: python
with container.enter_scope(Scope.REQUEST) as request_resolver:
service = request_resolver.resolve(Service)
"""
APP: BaseScope = field(default=BaseScope(1))
SESSION: BaseScope = field(default=BaseScope(2, skippable=True))
REQUEST: BaseScope = field(default=BaseScope(3))
ACTION: BaseScope = field(default=BaseScope(4))
STEP: BaseScope = field(default=BaseScope(5))
Scope = Scopes()
"""Provide the default scope constants used by container APIs.
Examples:
.. code-block:: python
with container.enter_scope(Scope.REQUEST) as request_resolver:
handler = request_resolver.resolve(Handler)
"""