Source code for autowire.context

"""
autowire.base
=============

Base definitions of autowire.

"""
from __future__ import annotations

import collections
import contextlib
import sys
from typing import (
    Any,
    ContextManager,
    Iterator,
    List,
    Optional,
    Sequence,
    Tuple,
    TypeVar,
)

from autowire.base_container import BaseContainer
from autowire.base_resource import BaseResource
from autowire.provider import ResourceProvider

R = TypeVar("R")


[docs]class NotPooled(Exception): """ Internal class for fiding pooled resource """ pass
[docs]class Context(ResourceProvider, ContextManager["Context"]): """ Resource management context base class """ def __init__(self, container: BaseContainer, parent: Optional[Context]): super().__init__() self.container = container self.parent = parent self.resource_pool: collections.OrderedDict[ BaseResource[Any], Tuple[Any, ContextManager[Any]] ] = collections.OrderedDict() self.children: List[Context] = []
[docs] def drain(self): """ Drain all resources resolved by this context. """ def children_drainer(children: List[Context]): if not children: return child = children.pop(0) try: children_drainer(children) finally: child.drain() items = list(self.resource_pool.items()) def drainer( items: list[BaseResource[Any], Tuple[Any, ContextManager[Any]]] ): if not items: # Drain all children children_drainer(self.children) return resource, (resolve, manager) = items.pop(0) try: drainer(items) except Exception: type_, value, traceback = sys.exc_info() self.resource_pool.pop(resource) manager.__exit__(type_, value, traceback) raise else: self.resource_pool.pop(resource) manager.__exit__(None, None, None) drainer(items)
[docs] @contextlib.contextmanager def child(self, preload: Sequence[BaseResource] = ()) -> Iterator[Context]: """ Create a child context :: with context.child() as child: value = child.resolve(resource) ... :param preload: resources to be preloaded """ with Context(self.container, self) as child: self.children.append(child) # Preload for resource in preload: child.resolve(resource) yield child
# # Resource provider implementation #
[docs] def resolve(self, resource: BaseResource[R]) -> R: """ Resolve resource in this context. """ try: return self._find_resource(resource) except NotPooled: pass # Create new resource if pooled resource not found impl = self.container.find_implementation(resource) manager = impl.reify(resource, self) resolved = manager.__enter__() # throw into resource pool self.resource_pool[resource] = (resolved, manager) return resolved
# # Context manager implementation # def __enter__(self): return self def __exit__(self, type_, value, traceback): self.drain() # # Privates # def _find_resource(self, resource: BaseResource[R]) -> R: # Find resolved resource from resource pool if resource in self.resource_pool: resolved, manager = self.resource_pool[resource] return resolved # Find from parent if self.parent is not None: return self.parent._find_resource(resource) else: raise NotPooled()