Skip to content

Commit

Permalink
Merge pull request hyperledger#3010 from dbluhm/feature/soft-binding
Browse files Browse the repository at this point in the history
feat: soft binding for plugin flexibility
  • Loading branch information
dbluhm committed Jun 6, 2024
2 parents 4c49252 + 44eb25b commit 44555f9
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 3 deletions.
2 changes: 1 addition & 1 deletion aries_cloudagent/askar/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def bind_providers(self):
"aries_cloudagent.indy.credx.issuer.IndyCredxIssuer", ref(self)
),
)
injector.bind_provider(
injector.soft_bind_provider(
VCHolder,
ClassProvider(
"aries_cloudagent.storage.vc_holder.askar.AskarVCHolder",
Expand Down
22 changes: 20 additions & 2 deletions aries_cloudagent/config/injector.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Standard Injector implementation."""

from typing import Mapping, Optional, Type
from typing import Dict, Mapping, Optional, Type

from .base import BaseProvider, BaseInjector, InjectionError, InjectType
from .provider import InstanceProvider, CachedProvider
Expand All @@ -18,7 +18,7 @@ def __init__(
):
"""Initialize an `Injector`."""
self.enforce_typing = enforce_typing
self._providers = {}
self._providers: Dict[Type, BaseProvider] = {}
self._settings = Settings(settings)

@property
Expand All @@ -45,6 +45,24 @@ def bind_provider(
provider = CachedProvider(provider)
self._providers[base_cls] = provider

def soft_bind_instance(self, base_cls: Type[InjectType], instance: InjectType):
"""Add a static instance as a soft class binding.
The binding occurs only if a provider for the same type does not already exist.
"""
if not self.get_provider(base_cls):
self.bind_instance(base_cls, instance)

def soft_bind_provider(
self, base_cls: Type[InjectType], provider: BaseProvider, *, cache: bool = False
):
"""Add a dynamic instance resolver as a soft class binding.
The binding occurs only if a provider for the same type does not already exist.
"""
if not self.get_provider(base_cls):
self.bind_provider(base_cls, provider, cache=cache)

def clear_binding(self, base_cls: Type[InjectType]):
"""Remove a previously-added binding."""
if base_cls in self._providers:
Expand Down
33 changes: 33 additions & 0 deletions aries_cloudagent/config/tests/test_injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,39 @@ def test_inject_provider(self):
assert mock_provider.settings[self.test_key] == override_settings[self.test_key]
assert mock_provider.injector is self.test_instance

def test_inject_soft_provider_bindings(self):
"""Test injecting providers with soft binding."""
provider = MockProvider(self.test_value)
override = MockProvider("Override")

self.test_instance.soft_bind_provider(str, provider)
assert self.test_instance.inject(str) == self.test_value

self.test_instance.clear_binding(str)
# Bound by a plugin on startup, for example
self.test_instance.bind_provider(str, override)

# Bound later in Profile.bind_providerse
self.test_instance.soft_bind_provider(str, provider)

# We want the plugin value, not the Profile bound value
assert self.test_instance.inject(str) == "Override"

def test_inject_soft_instance_bindings(self):
"""Test injecting providers with soft binding."""
self.test_instance.soft_bind_instance(str, self.test_value)
assert self.test_instance.inject(str) == self.test_value

self.test_instance.clear_binding(str)
# Bound by a plugin on startup, for example
self.test_instance.bind_instance(str, "Override")

# Bound later in Profile.bind_providerse
self.test_instance.soft_bind_instance(str, self.test_value)

# We want the plugin value, not the Profile bound value
assert self.test_instance.inject(str) == "Override"

def test_bad_provider(self):
"""Test empty and invalid provider results."""
self.test_instance.bind_provider(str, MockProvider(None))
Expand Down

0 comments on commit 44555f9

Please sign in to comment.