Skip to content

Commit

Permalink
Function collections (#156)
Browse files Browse the repository at this point in the history
* Add FunctionCollection as a type

* wip

* 'Refactored by Sourcery' (#157)

Co-authored-by: Sourcery AI <>

* wip

* wip

* tested definitions for function collections

* changelog update

* removed the magic. I didn't like it

* fix some merge mess up in the changelog

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
  • Loading branch information
meadsteve and sourcery-ai[bot] committed Aug 16, 2021
1 parent 9d10522 commit 4d577db
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

### Enhancements
* container.partial now works for instance methods. Thanks to @LeafyLappa for pointing out this didn't work.
* Added FunctionCollection type. Allows the container to store a collection of functions

### Bug Fixes
* container.partial now works for instance methods. Thanks to @LeafyLappa for pointing out this didn't work.
Expand Down
8 changes: 5 additions & 3 deletions lagom/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
"""Lagom, a type based dependency injection container"""
from .version import __version__
from .debug import get_build_info
from .definitions import Singleton, Alias, UnresolvableTypeDefinition
from .container import Container, ExplicitContainer
from .debug import get_build_info
from .decorators import (
bind_to_container,
magic_bind_to_container,
dependency_definition,
)
from .definitions import Singleton, Alias, UnresolvableTypeDefinition
from .markers import injectable
from .util.functional import FunctionCollection
from .version import __version__

__all__ = [
"__version__",
Expand All @@ -18,6 +19,7 @@
"UnresolvableTypeDefinition",
"Container",
"ExplicitContainer",
"FunctionCollection",
"bind_to_container",
"magic_bind_to_container",
"dependency_definition",
Expand Down
4 changes: 2 additions & 2 deletions lagom/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ def normalise(
if isinstance(resolver, SpecialDepDefinition):
return resolver
elif inspect.isfunction(resolver):
return construction(resolver)
return construction(resolver) # type: ignore
elif inspect.iscoroutinefunction(resolver):
return construction(resolver)
return construction(resolver) # type: ignore
elif inspect.isclass(resolver):
return Alias(resolver, skip_alias_definitions)
else:
Expand Down
34 changes: 33 additions & 1 deletion lagom/util/functional.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Code to help understand functions
"""
import inspect
from typing import Callable
from typing import Callable, TypeVar, Generic, Iterator


def arity(func: Callable) -> int:
Expand All @@ -16,3 +16,35 @@ def arity(func: Callable) -> int:
:return:
"""
return len(inspect.signature(func).parameters)


F = TypeVar("F", bound=Callable)


class FunctionCollection(Generic[F]):
"""
Represents a collection of functions that is hashable.
"""

def __init__(self, *checkers: F):
self._checkers = checkers
self.hash = hash(tuple(self._checkers))

def __len__(self) -> int:
return len(self._checkers)

def __contains__(self, item) -> bool:
return item in self._checkers

def __iter__(self) -> Iterator[F]:
return iter(self._checkers)

def __hash__(self):
return self.hash

def __eq__(self, other):
if isinstance(other, FunctionCollection):
return self.hash == other.hash
if isinstance(other, list):
return tuple(other) == self._checkers
return False
21 changes: 21 additions & 0 deletions tests/test_function_collections.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Callable

from lagom import Container, FunctionCollection


def _func_a(input: str) -> str:
return f"func_a({input})"


def _func_b(input: str) -> str:
return f"func_b({input})"


SomeSignature = Callable[[str], str]


def test_a_function_collection_can_be_used_by_a_container(
container: Container,
):
container[FunctionCollection[SomeSignature]] = FunctionCollection(_func_a, _func_b)
assert container[FunctionCollection[SomeSignature]] == [_func_a, _func_b]
23 changes: 22 additions & 1 deletion tests/util/test_functional.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import pytest

from lagom.util.functional import arity
from lagom.util.functional import arity, FunctionCollection


def _func_a():
pass


def _func_b():
pass


@pytest.mark.parametrize(
Expand All @@ -9,3 +17,16 @@
)
def test_we_can_get_arity_from_functions(test_func, expected_arity: int):
assert arity(test_func) == expected_arity


def test_function_collections_are_the_same_if_they_have_the_same_functions():
assert FunctionCollection(_func_a, _func_b) == FunctionCollection(_func_a, _func_b)


def test_function_collections_have_equality_to_a_list_of_those_functions():
assert FunctionCollection(_func_a, _func_b) == [_func_a, _func_b]


def test_function_collections_are_iterable():
collected_funcs = [func for func in FunctionCollection(_func_a, _func_b)]
assert collected_funcs == [_func_a, _func_b]

0 comments on commit 4d577db

Please sign in to comment.