Completed
Push — master ( a73913...a94802 )
by Max
13s queued 11s
created

_placeholder_kwargs()   A

Complexity

Conditions 3

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 13
nop 1
dl 0
loc 19
rs 9.75
c 0
b 0
f 0
1
import functools
2
import inspect
3
4
from ... import _class_placeholder
5
from ... import _pep_570_when
6
from .. import matchable
7
from ..patterns import mapping_match
8
from . import common
9
10
11
def _varargs(signature):
12
    for parameter in signature.parameters.values():
13
        if parameter.kind is inspect.Parameter.VAR_POSITIONAL:
14
            yield parameter
15
16
17
def _dispatch(func, matches, bound_args, bound_kwargs):
18
    for key, value in matches.items():
19
        if key in bound_kwargs:
20
            raise TypeError
21
        bound_kwargs[key] = value
22
    function_sig = inspect.signature(func)
23
    function_args = function_sig.bind(**bound_kwargs)
24
    for parameter in _varargs(function_sig):
25
        function_args.arguments[parameter.name] = bound_args
26
    function_args.apply_defaults()
27
    return func(*function_args.args, **function_args.kwargs)
28
29
30
class Function(common.Descriptor):
31
    """Decorator with value-based dispatch. Acts as a function."""
32
33
    def __init__(self, func, *args, **kwargs):
34
        del func
35
        super().__init__(*args, **kwargs)
36
        self.matchers = []
37
38
    def _matchers(self):
39
        yield self.matchers
40
41
    def __get__(self, instance, owner):
42
        if instance is None:
43
            return self
44
        return functools.partial(self, instance)
45
46
    def _bound_and_values(self, args, kwargs):
47
        # Then we figure out what signature we're giving the outside world.
48
        signature = inspect.signature(self)
49
        # The signature lets us regularize the call and apply any defaults
50
        bound_arguments = signature.bind(*args, **kwargs)
51
        bound_arguments.apply_defaults()
52
53
        # Extract the *args and **kwargs, if any.
54
        # These are never used in the matching, just passed to the underlying function
55
        bound_args = ()
56
        bound_kwargs = {}
57
        values = bound_arguments.arguments.copy()
58
        for parameter in signature.parameters.values():
59
            if parameter.kind is inspect.Parameter.VAR_POSITIONAL:
60
                bound_args = values.pop(parameter.name)
61
            if parameter.kind is inspect.Parameter.VAR_KEYWORD:
62
                bound_kwargs = values.pop(parameter.name)
63
        return bound_args, bound_kwargs, values
64
65
    def __call__(*args, **kwargs):
66
        # Okay, so, this is a convoluted mess.
67
        # First, we extract self from the beginning of the argument list
68
        self, *args = args
69
70
        bound_args, bound_kwargs, values = self._bound_and_values(args, kwargs)
71
72
        matchable_ = matchable.Matchable(values)
73
        for structure, func in self.matchers:
74
            if matchable_(structure):
75
                return _dispatch(func, matchable_.matches, bound_args, bound_kwargs)
76
        raise ValueError(values)
77
78
    @_pep_570_when.pep_570_when
79
    def when(self, kwargs):
80
        """Add a binding for this function."""
81
        return common.decorate(self.matchers, _placeholder_kwargs(kwargs))
82
83
84
def _kwarg_structure(kwargs):
85
    return mapping_match.DictPattern(kwargs, exhaustive=True)
86
87
88
def _placeholder_kwargs(kwargs):
89
    if any(_class_placeholder.is_placeholder(kwarg) for kwarg in kwargs.values()):
90
91
        @_class_placeholder.placeholder
92
        def _placeholder(cls):
93
            return _kwarg_structure(
94
                {
95
                    name: (
96
                        kwarg(cls)
97
                        if _class_placeholder.is_placeholder(kwarg)
98
                        else kwarg
99
                    )
100
                    for (name, kwarg) in kwargs.items()
101
                }
102
            )
103
104
        return _placeholder
105
106
    return _kwarg_structure(kwargs)
107