jacked.matchers._callable.CallableMatcher.match()   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 18
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 18
nop 4
dl 0
loc 18
rs 8.5666
c 0
b 0
f 0
1
"""
2
PRIVATE MODULE: do not import (from) it directly.
3
4
This module contains the ``CallableMatcher``class.
5
"""
6
import inspect
7
from typing import Callable, Tuple, Any, Awaitable
8
from jacked._compatibility_impl import get_args_and_return_type
9
from jacked._injectable import Injectable
10
from jacked._container import Container
11
from jacked._typing import NoneType, issubtype
12
from jacked.matchers._base_matcher import BaseMatcher
13
14
15
class CallableMatcher(BaseMatcher):
16
17
    def match(
18
            self,
19
            hint: object,
20
            injectable: Injectable,
21
            container: Container):
22
        if inspect.isfunction(injectable.subject):
23
            params_hint, return_hint = get_args_and_return_type(hint)
24
            return_hint = (inspect.Signature.empty if return_hint is NoneType
25
                           else return_hint)
26
            signature = inspect.signature(injectable.subject)
27
            params_injectable = tuple([signature.parameters[x].annotation
28
                                       for x in signature.parameters])
29
            return_injectable = signature.return_annotation
30
            if inspect.iscoroutinefunction(injectable.subject):
31
                return_injectable = Awaitable[return_injectable]
32
            if (self._params_match(params_hint, params_injectable)
33
                    and self._compatible_with(return_injectable, return_hint)):
34
                return injectable.subject
35
36
    def _matching_type(self):
37
        return Callable
38
39
    def _params_match(self, params_hint: Tuple[type, ...],
40
                      params_injectable: Tuple[type, ...]) -> bool:
41
        if len(params_hint) != len(params_injectable):
42
            return False
43
        for i, _ in enumerate(params_hint):
44
            if not self._compatible_with(params_injectable[i], params_hint[i]):
45
                return False
46
        return True
47
48
    def _compatible_with(self, t1: type, t2: type):
49
        return t2 is Any or issubtype(t1, t2)
50