Skip to content

Commit

Permalink
Implement method 'extend'
Browse files Browse the repository at this point in the history
  • Loading branch information
mediapills committed Aug 22, 2021
1 parent fbd8ba3 commit 6fa78a7
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 29 deletions.
26 changes: 13 additions & 13 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@ object:
.. code-block:: python
# define some services
injector['session_storage'] = lambda i: (
injector['session_storage'] = lambda di: (
SessionStorage('SESSION_ID')
)
injector['session'] = lambda i: (
Session(i['session_storage'])
injector['session'] = lambda di: (
Session(di['session_storage'])
)
Notice that the anonymous function has access to the current injector
Expand Down Expand Up @@ -194,8 +194,8 @@ anonymous function with the ``factory()`` method

.. code-block:: python
injector['session'] = injector.factory(lambda i: (
Session(i['session_storage'])
injector['session'] = injector.factory(lambda di: (
Session(di['session_storage'])
))
Now, each call to ``injector['session']`` returns a new instance of the
Expand All @@ -217,8 +217,8 @@ If you change the ``session_storage`` service definition like below:

.. code-block:: python
injector['session_storage'] = lambda i: (
i['session_storage_cls'](i['cookie_name'])
injector['session_storage'] = lambda di: (
di['session_storage_cls'](di['cookie_name'])
)
You can now easily change the cookie name by overriding the
Expand Down Expand Up @@ -246,12 +246,12 @@ run on your service just after it is created:

.. code-block:: python
injector['session_storage'] = lambda i: (
i['session_storage_class'](i['cookie_name'])
injector['session_storage'] = lambda di: (
di['session_storage_class'](di['cookie_name'])
)
def session_storage_ext(storage, i):
# Do something with storage
def session_storage_ext(storage: Callable, di: Injector):
# Do something with base storage using di
return storage
Expand All @@ -269,8 +269,8 @@ raw access to this function, you can use the ``raw()`` method:

.. code-block:: python
injector['session'] = lambda c: (
Session(c['session_storage'])
injector['session'] = lambda di: (
Session(di['session_storage'])
)
sessionFunction = container.raw('session')
29 changes: 23 additions & 6 deletions src/mediapills/dependency_injection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from typing import Union

from mediapills.dependency_injection.exceptions import FrozenServiceException
from mediapills.dependency_injection.exceptions import InvalidServiceIdentifierException
from mediapills.dependency_injection.exceptions import ProtectedServiceException
from mediapills.dependency_injection.exceptions import UnknownIdentifierException

__all__ = ["Injector"]
Expand All @@ -32,10 +34,11 @@ class Injector(dict): # type: ignore
def __init__(self, *args, **kw) -> None: # type: ignore
super().__init__(*args, **kw)

self._warmed: bool = False
self._raw: Dict[Any, Any] = dict()
self._protected: Set[Any] = set()
self._frozen: Set[Any] = set()
self._raw: Dict[Any, Any] = dict()
self._warmed: bool = False
self._factories: Dict[str, Any] = dict()

def warm_up(self) -> None:
"""process all offsets."""
Expand Down Expand Up @@ -86,6 +89,7 @@ def __delitem__(self, key: Any) -> None:
self._protected.discard(key)
self._frozen.discard(key)
self._raw.pop(key, None)
self._factories.pop(key, None)

dict.__delitem__(self, key)

Expand All @@ -97,6 +101,7 @@ def clear(self) -> None:
self._protected.clear()
self._frozen.clear()
self._raw.clear()
self._factories.clear()

dict.clear(self)

Expand Down Expand Up @@ -147,10 +152,22 @@ def protect(self, key: Any) -> None:

def extend(self, key: Any, callable: Callable[[Any], Any]) -> None: # dead: disable
"""Extends an object definition."""
if key not in self:
raise UnknownIdentifierException(key)

if key in self._frozen:
raise FrozenServiceException(key)

if key in self._protected:
raise ProtectedServiceException(key)

raw = self.raw(key)

if not hasattr(raw, "__call__"):
raise InvalidServiceIdentifierException(key)

raise NotImplementedError
extended = lambda di, func=callable, base=raw: func(base(di), di) # noqa: E731

def factory(self, callable: Callable[[Any], Any]) -> None: # dead: disable
"""Marks a callable as being a factory service."""
self._factories[key] = extended

raise NotImplementedError
self.__setitem__(key, extended)
17 changes: 11 additions & 6 deletions src/mediapills/dependency_injection/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
class BaseContainerException(Exception):
class BaseInjectorException(Exception):

pass


class ExpectedInvokableException(BaseContainerException): # dead: disable
class ExpectedInvokableException(BaseInjectorException): # dead: disable

pass


class FrozenServiceException(BaseContainerException):
class FrozenServiceException(BaseInjectorException):

pass


class InvalidServiceIdentifierException(BaseContainerException): # dead: disable
class ProtectedServiceException(BaseInjectorException):

pass


class UnknownIdentifierException(BaseContainerException, KeyError):
class InvalidServiceIdentifierException(BaseInjectorException):

pass


class UnknownIdentifierException(BaseInjectorException, KeyError):

pass


class RecursionInfiniteLoopError( # dead: disable
BaseContainerException, RecursionError
BaseInjectorException, RecursionError
):
pass
52 changes: 48 additions & 4 deletions tests/unit/test_injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

from mediapills.dependency_injection import Injector
from mediapills.dependency_injection.exceptions import FrozenServiceException
from mediapills.dependency_injection.exceptions import InvalidServiceIdentifierException
from mediapills.dependency_injection.exceptions import ProtectedServiceException
from mediapills.dependency_injection.exceptions import UnknownIdentifierException

DATA_TYPES_PARAMETRIZED_INPUT = [
("str", "value"), # Check text type (str)
Expand Down Expand Up @@ -176,7 +179,7 @@ def test_update_should_raise_error(self) -> None:


class TestInjector(unittest.TestCase):
def test_warmed_raw_should_be_same_as_init(self) -> None:
def test_raw_warmed_should_be_same_as_initial(self) -> None:
func = lambda i: "test" # noqa: E731

obj = Injector()
Expand All @@ -186,15 +189,15 @@ def test_warmed_raw_should_be_same_as_init(self) -> None:

self.assertEqual(func, obj.raw("func"))

def test_cold_raw_should_be_same_as_init(self) -> None:
def test_raw_cold_should_be_same_as_initial(self) -> None:
func = lambda i: "test" # noqa: E731

obj = Injector()
obj["func"] = func

self.assertEqual(func, obj.raw("func"))

def test_protected_should_return_different(self) -> None:
def test_call_protected_should_return_different(self) -> None:
obj = Injector()
obj["func"] = lambda i: "time: '{}', rnad: '{}'".format(
time.time() * 1000, random()
Expand All @@ -203,11 +206,52 @@ def test_protected_should_return_different(self) -> None:

self.assertNotEqual(obj["func"], obj["func"])

def test_non_protected_should_return_same(self) -> None:
def test_call_non_protected_should_return_same(self) -> None:
obj = Injector()
obj["func"] = lambda i: "time: '{}', rnad: '{}'".format(
time.time() * 1000, random()
)

self.assertEqual(obj["func"], obj["func"])

def test_extend_nonexistent_should_raise_error(self) -> None:

obj = Injector()

with self.assertRaises(UnknownIdentifierException):
obj.extend("any", lambda i: "error")

def test_extend_frozen_should_raise_error(self) -> None:

obj = Injector()
obj["any"] = lambda i: "test"
_ = obj["any"]

with self.assertRaises(FrozenServiceException):
obj.extend("any", lambda i: "error")

def test_extend_protected_should_raise_error(self) -> None:

obj = Injector()
obj["any"] = lambda i: "test"
obj.protect("any")

with self.assertRaises(ProtectedServiceException):
obj.extend("any", lambda i: "error")

def test_extend_scalar_should_raise_error(self) -> None:

obj = Injector()
obj["any"] = "test"

with self.assertRaises(InvalidServiceIdentifierException):
obj.extend("any", lambda i: "error")

def test_extend_should_ok(self) -> None:

obj = Injector()
obj["any"] = lambda i: "base"

obj.extend("any", lambda base, di: "extended " + base)

self.assertEqual("extended base", obj["any"])

0 comments on commit 6fa78a7

Please sign in to comment.