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

feat: inject profile and session #2997

Merged
merged 3 commits into from
May 31, 2024
Merged
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
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
Loading