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
|
|
|
def exhaustive_length_must_match(target, value_match_dict): |
11
|
|
|
if target.exhaustive and len(value_match_dict) != len(target.match_dict): |
12
|
|
|
raise MatchFailure |
13
|
|
|
|
14
|
|
|
|
15
|
|
|
class AttrPattern(CompoundMatch, tuple): |
16
|
|
|
"""A matcher that destructures an object using attribute access.""" |
17
|
|
|
|
18
|
|
|
__slots__ = () |
19
|
|
|
|
20
|
|
|
def __new__(*args, **kwargs): |
21
|
|
|
cls, *args = args |
22
|
|
|
if args: |
23
|
|
|
raise ValueError(args) |
24
|
|
|
return super(AttrPattern, cls).__new__(cls, (tuple(kwargs.items()),)) |
25
|
|
|
|
26
|
|
|
@property |
27
|
|
|
def match_dict(self): |
28
|
|
|
return self[0] |
29
|
|
|
|
30
|
|
|
def destructure(self, value): |
31
|
|
|
if isinstance(value, AttrPattern): |
32
|
|
|
value_cant_be_smaller(self.match_dict, value.match_dict) |
33
|
|
|
if value.match_dict: |
34
|
|
|
first_match, *remainder = value.match_dict |
35
|
|
|
return (AttrPattern(**dict(remainder)), first_match[1]) |
|
|
|
|
36
|
|
|
elif self.match_dict: |
37
|
|
|
first_match = self.match_dict[0] |
38
|
|
|
try: |
39
|
|
|
return (value, getattr(value, first_match[0])) |
40
|
|
|
except AttributeError: |
41
|
|
|
raise MatchFailure |
42
|
|
|
return () |
43
|
|
|
|
44
|
|
|
|
45
|
|
|
class DictPattern(CompoundMatch, tuple): |
46
|
|
|
"""A matcher that destructures a dictionary by key.""" |
47
|
|
|
|
48
|
|
|
__slots__ = () |
49
|
|
|
|
50
|
|
|
def __new__(cls, match_dict, *, exhaustive=False): |
51
|
|
|
return super(DictPattern, cls).__new__( |
52
|
|
|
cls, (tuple(match_dict.items()), exhaustive) |
53
|
|
|
) |
54
|
|
|
|
55
|
|
|
@property |
56
|
|
|
def match_dict(self): |
57
|
|
|
return self[0] |
58
|
|
|
|
59
|
|
|
@property |
60
|
|
|
def exhaustive(self): |
61
|
|
|
return self[1] |
62
|
|
|
|
63
|
|
|
def destructure(self, value): |
64
|
|
|
if isinstance(value, DictPattern): |
65
|
|
|
value_cant_be_smaller(self.match_dict, value.match_dict) |
66
|
|
|
exhaustive_length_must_match(self, value.match_dict) |
67
|
|
|
if value.match_dict: |
68
|
|
|
first_match, *remainder = value.match_dict |
69
|
|
|
return (DictPattern(dict(remainder)), first_match[1]) |
|
|
|
|
70
|
|
|
elif self.match_dict: |
71
|
|
|
exhaustive_length_must_match(self, value) |
72
|
|
|
first_match = self.match_dict[0] |
73
|
|
|
try: |
74
|
|
|
return (value, value[first_match[0]]) |
75
|
|
|
except KeyError: |
76
|
|
|
raise MatchFailure |
77
|
|
|
exhaustive_length_must_match(self, value) |
78
|
|
|
return () |
79
|
|
|
|