Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dependency Injection: build mechanics for contextualized scope #78

Open
lhaze opened this issue Sep 8, 2020 · 0 comments
Open

Dependency Injection: build mechanics for contextualized scope #78

lhaze opened this issue Sep 8, 2020 · 0 comments

Comments

@lhaze
Copy link
Collaborator

lhaze commented Sep 8, 2020

A contextualized scope is the scope that has its context bound to some related object. A request scope has its context bound to some request, and session or a thread one -- accordingly.

Let's refine the key DI concepts:

  • request is describing what is needed
  • resolution is describing the way to construct the instance of a dependency
  • scope is describing how many instances to construct and how long should they live
  • context is describing how the instance is related to its scope
from dataclasses import dataclass
import typing as t
from enum import Enum
from functools import partial

from pca.utils.dependency_injection import Container

Constructor = t.Union[t.Type, t.Callable]
Kwargs = t.Dict[str, t.Any]  # keyword-arguments of a Constructor
# empty interfaces, because for now no other requirements are known
IRequest = t.NewType('IRequest', object)
ISession = t.NewType('ISession', object)


@dataclass(frozen=True)
class DIRequest:
    """
    Describes the way a component might be requested from the Container.

    It can be at one of two states:
    * indeterminate - the context value will become determined when you bound its value to
        the state of some target (a DI component) with `DIRequest.determine` method.
    * determined - the context is static, nothing is to be determined.

    You can check the container with the DIRequest only when the context is determined.

    NB: DIRequest is immutable, so calling `DIRequest.determine` on a indeterminate context will
        produce a new DIRequest instance.

    :cvar interface: an interface of the dependency. It's often used along with annotations about
        the variable/argument that requests the dependency.
    :cvar name: an arbitrary string describing dependency (an alternative to `interface` argument).
        Simple to use, but it doesn't give any information about its interface.
    :cvar qualifier: an object of any type that adds a finer grade granularity about
        the dependency; i.e. you may want a component of `IDao` interface: not just anyone, but
        the one that is modeling some specific schema; the qualifier describes the latter
        condition.
    :cvar get_qualifier: a function that can produce a qualifier from a component. Signature of
        (target: Component) -> qualifier: str
    """
    name: str = None
    interface: t.Type = None
    qualifier: t.Any = None
    get_qualifier: t.Callable[[t.Any], t.Any] = None


class Scopes(Enum):
    """
    Describes how often instances should be constructed and how long should they live.

    INSTANCE: every time the dependency is requested, a new instance is created
    REQUEST: once per request, whatever might mean the concept of 'request'
    SESSION: once per session, whatever might mean the concept of 'session'
    THREAD: once per thread, like in multithreading module
    SINGLETON: once per its container lifetime
    """
    INSTANCE: 'Scopes' = partial(Container.instance_scope)
    REQUEST: 'Scopes' = partial(Container.request_scope)
    SESSION: 'Scopes' = partial(Container.session_scope)
    THREAD: 'Scopes' = partial(Container.thread_scope)
    SINGLETON: 'Scopes' = partial(Container.singleton_scope)


@dataclass(frozen=True)
class DIContext:
    """
    Describes how the instance is related to its scope:

    :cvar request_object: an identifier of the request bound (iff it exists)
    :cvar session_object: an identifier of the session bound (iff it exists)
    """
    request: DIRequest
    request_object: t.Optional[RequestIdentifier] = None
    session_object: t.Optional[SessionIdentifier] = None


@dataclass(frozen=True)
class DIResolution:
    """
    Describes what has been registered as a dependency:

    :cvar constructor: a type or a callable that builds the dependency instance
    :cvar kwargs: a dict that specifies keyword arguments for the dependency constructor
    """
    constructor: Constructor
    kwargs: Kwargs = None
@lhaze lhaze added this to To do in Dependency Injection via automation Sep 8, 2020
@lhaze lhaze added this to the 0.1 milestone Sep 8, 2020
@lhaze lhaze self-assigned this Sep 13, 2020
@lhaze lhaze moved this from To do to In progress in Dependency Injection Aug 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Dependency Injection
  
In progress
Development

No branches or pull requests

1 participant