|
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
|
|
|
|