Skip to content

Commit

Permalink
Merge pull request #2997 from dbluhm/feature/inject-profile-redo
Browse files Browse the repository at this point in the history
feat: inject profile and session
  • Loading branch information
dbluhm authored May 31, 2024
2 parents 1b801e3 + 94d41be commit 854b7a5
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 87 deletions.
16 changes: 8 additions & 8 deletions aries_cloudagent/admin/request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ def __init__(
self,
profile: Profile,
*,
context: InjectionContext = None,
settings: Mapping[str, object] = None,
root_profile: Profile = None,
metadata: dict = None
context: Optional[InjectionContext] = None,
settings: Optional[Mapping[str, object]] = None,
root_profile: Optional[Profile] = None,
metadata: Optional[dict] = None
):
"""Initialize an instance of AdminRequestContext."""
self._context = (context or profile.context).start_scope("admin", settings)
self._context = (context or profile.context).start_scope(settings)
self._profile = profile
self._root_profile = root_profile
self._metadata = metadata
Expand Down Expand Up @@ -72,7 +72,7 @@ def transaction(self) -> ProfileSession:
def inject(
self,
base_cls: Type[InjectType],
settings: Mapping[str, object] = None,
settings: Optional[Mapping[str, object]] = None,
) -> InjectType:
"""Get the provided instance of a given class identifier.
Expand All @@ -89,7 +89,7 @@ def inject(
def inject_or(
self,
base_cls: Type[InjectType],
settings: Mapping[str, object] = None,
settings: Optional[Mapping[str, object]] = None,
default: Optional[InjectType] = None,
) -> Optional[InjectType]:
"""Get the provided instance of a given class identifier or default if not found.
Expand All @@ -111,7 +111,7 @@ def update_settings(self, settings: Mapping[str, object]):

@classmethod
def test_context(
cls, session_inject: dict = None, profile: Profile = None
cls, session_inject: Optional[dict] = None, profile: Optional[Profile] = None
) -> "AdminRequestContext":
"""Quickly set up a new admin request context for tests."""
ctx = AdminRequestContext(profile or IN_MEM.resolved.test_profile())
Expand Down
47 changes: 7 additions & 40 deletions aries_cloudagent/config/injection_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ class InjectionContext(BaseInjector):
ROOT_SCOPE = "application"

def __init__(
self, *, settings: Mapping[str, object] = None, enforce_typing: bool = True
self,
*,
settings: Optional[Mapping[str, object]] = None,
enforce_typing: bool = True
):
"""Initialize a `ServiceConfig`."""
self._injector = Injector(settings, enforce_typing=enforce_typing)
self._scope_name = InjectionContext.ROOT_SCOPE
self._scopes = []

@property
def injector(self) -> Injector:
Expand All @@ -38,16 +40,6 @@ def injector(self, injector: Injector):
"""Setter for scope-specific injector."""
self._injector = injector

@property
def scope_name(self) -> str:
"""Accessor for the current scope name."""
return self._scope_name

@scope_name.setter
def scope_name(self, scope_name: str):
"""Accessor for the current scope name."""
self._scope_name = scope_name

@property
def settings(self) -> Settings:
"""Accessor for scope-specific settings."""
Expand All @@ -64,7 +56,7 @@ def update_settings(self, settings: Mapping[str, object]):
self.injector.settings.update(settings)

def start_scope(
self, scope_name: str, settings: Optional[Mapping[str, object]] = None
self, settings: Optional[Mapping[str, object]] = None
) -> "InjectionContext":
"""Begin a new named scope.
Expand All @@ -76,39 +68,15 @@ def start_scope(
A new injection context representing the scope
"""
if not scope_name:
raise InjectionContextError("Scope name must be non-empty")
if self._scope_name == scope_name:
raise InjectionContextError("Cannot re-enter scope: {}".format(scope_name))
for scope in self._scopes:
if scope.name == scope_name:
raise InjectionContextError(
"Cannot re-enter scope: {}".format(scope_name)
)
result = self.copy()
result._scopes.append(Scope(name=self.scope_name, injector=self.injector))
result._scope_name = scope_name
if settings:
result.update_settings(settings)
return result

def injector_for_scope(self, scope_name: str) -> Injector:
"""Fetch the injector for a specific scope.
Args:
scope_name: The unique scope identifier
"""
if scope_name == self.scope_name:
return self.injector
for scope in self._scopes:
if scope.name == scope_name:
return scope.injector
return None

def inject(
self,
base_cls: Type[InjectType],
settings: Mapping[str, object] = None,
settings: Optional[Mapping[str, object]] = None,
) -> InjectType:
"""Get the provided instance of a given class identifier.
Expand All @@ -125,7 +93,7 @@ def inject(
def inject_or(
self,
base_cls: Type[InjectType],
settings: Mapping[str, object] = None,
settings: Optional[Mapping[str, object]] = None,
default: Optional[InjectType] = None,
) -> Optional[InjectType]:
"""Get the provided instance of a given class identifier or default if not found.
Expand All @@ -145,5 +113,4 @@ def copy(self) -> "InjectionContext":
"""Produce a copy of the injector instance."""
result = copy.copy(self)
result._injector = self.injector.copy()
result._scopes = self._scopes.copy()
return result
9 changes: 6 additions & 3 deletions aries_cloudagent/config/injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ class Injector(BaseInjector):
"""Injector implementation with static and dynamic bindings."""

def __init__(
self, settings: Mapping[str, object] = None, *, enforce_typing: bool = True
self,
settings: Optional[Mapping[str, object]] = None,
*,
enforce_typing: bool = True,
):
"""Initialize an `Injector`."""
self.enforce_typing = enforce_typing
Expand Down Expand Up @@ -54,7 +57,7 @@ def get_provider(self, base_cls: Type[InjectType]):
def inject_or(
self,
base_cls: Type[InjectType],
settings: Mapping[str, object] = None,
settings: Optional[Mapping[str, object]] = None,
default: Optional[InjectType] = None,
) -> Optional[InjectType]:
"""Get the provided instance of a given class identifier or default if not found.
Expand Down Expand Up @@ -92,7 +95,7 @@ def inject_or(
def inject(
self,
base_cls: Type[InjectType],
settings: Mapping[str, object] = None,
settings: Optional[Mapping[str, object]] = None,
) -> InjectType:
"""Get the provided instance of a given class identifier.
Expand Down
32 changes: 3 additions & 29 deletions aries_cloudagent/config/tests/test_injection_context.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from unittest import IsolatedAsyncioTestCase

from ..base import InjectionError
from ..injection_context import InjectionContext, InjectionContextError
from ..injection_context import InjectionContext


class TestInjectionContext(IsolatedAsyncioTestCase):
Expand All @@ -14,39 +14,16 @@ def setUp(self):

def test_settings_init(self):
"""Test settings initialization."""
assert self.test_instance.scope_name == self.test_instance.ROOT_SCOPE
for key in self.test_settings:
assert key in self.test_instance.settings
assert self.test_instance.settings[key] == self.test_settings[key]

def test_simple_scope(self):
"""Test scope entrance and exit."""
with self.assertRaises(InjectionContextError):
self.test_instance.start_scope(None)
with self.assertRaises(InjectionContextError):
self.test_instance.start_scope(self.test_instance.ROOT_SCOPE)

injector = self.test_instance.injector_for_scope(self.test_instance.ROOT_SCOPE)
assert injector == self.test_instance.injector
assert self.test_instance.injector_for_scope("no such scope") is None

context = self.test_instance.start_scope(self.test_scope)
assert context.scope_name == self.test_scope
context.scope_name = "Bob"
assert context.scope_name == "Bob"

with self.assertRaises(InjectionContextError):
context.start_scope(self.test_instance.ROOT_SCOPE)
assert self.test_instance.scope_name == self.test_instance.ROOT_SCOPE

def test_settings_scope(self):
"""Test scoped settings."""
upd_settings = {self.test_key: "NEWVAL"}
context = self.test_instance.start_scope(self.test_scope, upd_settings)
context = self.test_instance.start_scope(upd_settings)
assert context.settings[self.test_key] == "NEWVAL"
assert self.test_instance.settings[self.test_key] == self.test_value
root = context.injector_for_scope(context.ROOT_SCOPE)
assert root.settings[self.test_key] == self.test_value

context.settings = upd_settings
assert context.settings == upd_settings
Expand All @@ -64,11 +41,8 @@ async def test_inject_simple(self):

async def test_inject_scope(self):
"""Test a scoped injection."""
context = self.test_instance.start_scope(self.test_scope)
context = self.test_instance.start_scope()
assert context.inject_or(str) is None
context.injector.bind_instance(str, self.test_value)
assert context.inject(str) is self.test_value
assert self.test_instance.inject_or(str) is None
root = context.injector_for_scope(context.ROOT_SCOPE)
assert root.inject_or(str) is None
assert self.test_instance.inject_or(str) is None
12 changes: 9 additions & 3 deletions aries_cloudagent/core/profile.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"""Classes for managing profile information within a request context."""

import logging
from abc import ABC, abstractmethod
import logging
from typing import Any, Mapping, Optional, Type
from weakref import ref

from ..config.base import InjectionError
from ..config.injection_context import InjectionContext
Expand Down Expand Up @@ -30,10 +31,13 @@ def __init__(
created: bool = False,
):
"""Initialize a base profile."""
self._context = context or InjectionContext()
self._created = created
self._name = name or Profile.DEFAULT_NAME

context = context or InjectionContext()
self._context = context.start_scope()
self._context.injector.bind_instance(Profile, ref(self))

@property
def backend(self) -> str:
"""Accessor for the backend implementation name."""
Expand Down Expand Up @@ -159,10 +163,12 @@ def __init__(
self._active = False
self._awaited = False
self._entered = 0
self._context = (context or profile.context).start_scope("session", settings)
self._context = (context or profile.context).start_scope(settings)
self._profile = profile
self._events = []

self._context.injector.bind_instance(ProfileSession, ref(self))

async def _setup(self):
"""Create the underlying session or transaction."""

Expand Down
6 changes: 3 additions & 3 deletions aries_cloudagent/messaging/request_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ def __init__(
self,
profile: Profile,
*,
context: InjectionContext = None,
settings: Mapping[str, object] = None
context: Optional[InjectionContext] = None,
settings: Optional[Mapping[str, object]] = None
):
"""Initialize an instance of RequestContext."""
self._connection_ready = False
self._connection_record = None
self._context = (context or profile.context).start_scope("request", settings)
self._context = (context or profile.context).start_scope(settings)
self._message = None
self._message_receipt = None
self._profile = profile
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ async def asyncSetUp(self):
"debug.auto_accept_invites": True,
"debug.auto_accept_requests": True,
"multitenant.enabled": True,
"wallet.id": True,
"wallet.id": "test-wallet-id",
},
bind={
BaseResponder: self.responder,
Expand Down

0 comments on commit 854b7a5

Please sign in to comment.