Passed
Push — master ( 1324f3...2cb96a )
by Max
01:33
created

dict_pattern_length()   A

Complexity

Conditions 2

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
from .._match_failure import MatchFailure
2
from .compound_match import CompoundMatch
3
4
5
def value_cant_be_smaller(target_match_dict, value_match_dict):
6
    if len(value_match_dict) < len(target_match_dict):
7
        raise MatchFailure
8
9
10
class AttrPattern(CompoundMatch, tuple):
11
    """A matcher that destructures an object using attribute access."""
12
13
    __slots__ = ()
14
15
    def __new__(*args, **kwargs):
16
        cls, *args = args
17
        if args:
18
            raise ValueError(args)
19
        return super(AttrPattern, cls).__new__(cls, (tuple(kwargs.items()),))
20
21
    @property
22
    def match_dict(self):
23
        return self[0]
24
25
    def destructure(self, value):
26
        if not self.match_dict:
27
            return ()
28
        if isinstance(value, AttrPattern):
29
            value_cant_be_smaller(self.match_dict, value.match_dict)
30
            first_match, *remainder = value.match_dict
31
            return (AttrPattern(**dict(remainder)), first_match[1])
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable remainder does not seem to be defined.
Loading history...
32
        first_match = self.match_dict[0]
33
        try:
34
            return (value, getattr(value, first_match[0]))
35
        except AttributeError:
36
            raise MatchFailure
37
38
39
def dict_pattern_length(dp_or_d):
40
    if isinstance(dp_or_d, DictPattern):
41
        return len(dp_or_d.match_dict)
42
    return len(dp_or_d)
43
44
45
def exhaustive_length_must_match(target, value):
46
    if target.exhaustive and dict_pattern_length(value) != dict_pattern_length(target):
47
        raise MatchFailure
48
49
50
class DictPattern(CompoundMatch, tuple):
51
    """A matcher that destructures a dictionary by key."""
52
53
    __slots__ = ()
54
55
    def __new__(cls, match_dict, *, exhaustive=False):
56
        return super(DictPattern, cls).__new__(
57
            cls, (tuple(match_dict.items()), exhaustive)
58
        )
59
60
    @property
61
    def match_dict(self):
62
        return self[0]
63
64
    @property
65
    def exhaustive(self):
66
        return self[1]
67
68
    def destructure(self, value):
69
        exhaustive_length_must_match(self, value)
70
        if not self.match_dict:
71
            return ()
72
        if isinstance(value, DictPattern):
73
            value_cant_be_smaller(self.match_dict, value.match_dict)
74
            first_match, *remainder = value.match_dict
75
            return (DictPattern(dict(remainder)), first_match[1])
1 ignored issue
show
Comprehensibility Best Practice introduced by
The variable remainder does not seem to be defined.
Loading history...
76
        first_match = self.match_dict[0]
77
        try:
78
            return (value, value[first_match[0]])
79
        except KeyError:
80
            raise MatchFailure
81