Passed
Push — master ( 499c95...ac8c66 )
by Max
48s
created

value_cant_be_smaller()   A

Complexity

Conditions 2

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nop 2
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
from ._adt_constructor import ADTConstructor
2
from ._match_failure import MatchFailure
3
from ._not_in import not_in
4
from ._patterns import AsPattern
5
from ._patterns import AttrPattern
6
from ._patterns import DictPattern
7
from ._patterns import Pattern
8
from ._unpack import unpack
9
10
11
class Destructurer:
12
13
    def __init__(self, target):
14
        self.target = target
15
16
    def __call__(self, value):
17
        return self.destructure(value)
18
19
    def destructure(self, value):
20
        raise NotImplementedError
21
22
    type = None
23
24
25
class AsPatternDestructurer(Destructurer):
26
27
    def destructure(self, value):
28
        if isinstance(value, AsPattern):
29
            if value is self.target:
30
                return reversed(value)
31
            return (value.match, value)
32
        return (value, value)
33
34
    type = AsPattern
35
36
37
class ADTDestructurer(Destructurer):
38
39
    def destructure(self, value):
40
        if value.__class__ is not self.target.__class__:
41
            raise MatchFailure
42
        return reversed(unpack(value))
43
44
    type = ADTConstructor
45
46
47
def guarded_getattr(value, target_key):
48
    try:
49
        return getattr(value, target_key)
50
    except AttributeError:
51
        raise MatchFailure
52
53
54
def value_cant_be_smaller(target_match_dict, value_match_dict):
55
    if len(value_match_dict) < len(target_match_dict):
56
        raise MatchFailure
57
58
59
def key_destructure(value, match_dict, getter):
60
    return [getter(value, target_key) for (target_key, _) in reversed(match_dict)]
61
62
63
def same_type_destructure(target_match_dict, value_match_dict):
64
    for (target_key, _), (value_key, value_value) in zip(target_match_dict, value_match_dict):
65
        if target_key != value_key:
66
            raise MatchFailure
67
        yield value_value
68
69
70
class AttrPatternDestructurer(Destructurer):
71
72
    def destructure(self, value):
73
        if isinstance(value, AttrPattern):
74
            value_cant_be_smaller(self.target.match_dict, value.match_dict)
75
            return reversed(list(same_type_destructure(self.target.match_dict, value.match_dict)))
76
        return key_destructure(value, self.target.match_dict, guarded_getattr)
77
78
    type = AttrPattern
79
80
81
def guarded_getitem(value, target_key):
82
    try:
83
        return value[target_key]
84
    except KeyError:
85
        raise MatchFailure
86
87
88
def exhaustive_length_must_match(target, value_match_dict):
89
    if target.exhaustive and len(value_match_dict) != len(target.match_dict):
90
        raise MatchFailure
91
92
93
class DictPatternDestructurer(Destructurer):
94
95
    def destructure(self, value):
96
        if isinstance(value, DictPattern):
97
            value_cant_be_smaller(self.target.match_dict, value.match_dict)
98
            exhaustive_length_must_match(self.target, value.match_dict)
99
            return reversed(list(same_type_destructure(self.target.match_dict, value.match_dict)))
100
        exhaustive_length_must_match(self.target, value)
101
        return key_destructure(value, self.target.match_dict, guarded_getitem)
102
103
    type = DictPattern
104
105
106
class TupleDestructurer(Destructurer):
107
108
    def destructure(self, value):
109
        if isinstance(value, self.target.__class__) and len(self.target) == len(value):
110
            return reversed(value)
111
        raise MatchFailure
112
113
    type = tuple
114
115
116
class DestructurerList(tuple):
117
118
    __slots__ = ()
119
120
    def __new__(cls, *destructurers):
121
        return super().__new__(cls, destructurers)
122
123
    def get_destructurer(self, item):
124
        for destructurer in self:
125
            if isinstance(item, destructurer.type):
126
                return destructurer(item)
127
        return None
128
129
    @classmethod
130
    def custom(cls, *destructurers):
131
        return cls(
132
            AsPatternDestructurer, ADTDestructurer, AttrPatternDestructurer, DictPatternDestructurer, *destructurers, TupleDestructurer)
133
134
    def names(self, target):
135
        name_list = []
136
        names_seen = set()
137
        to_process = [target]
138
        while to_process:
139
            item = to_process.pop()
140
            if isinstance(item, Pattern):
141
                not_in(names_seen, item.name)
142
                names_seen.add(item.name)
143
                name_list.append(item.name)
144
            else:
145
                destructurer = self.get_destructurer(item)
146
                if destructurer:
147
                    to_process.extend(destructurer(item))
148
        return name_list
149
150
151
DESTRUCTURERS = DestructurerList.custom()
152