Passed
Push — master ( 7a63ce...499c95 )
by Max
44s
created

DictPatternDestructurer.destructure()   B

Complexity

Conditions 7

Size

Total Lines 10
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 10
nop 2
dl 0
loc 10
rs 8
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 key_destructure(value, match_dict, getter):
55
    return [getter(value, target_key) for (target_key, _) in reversed(match_dict)]
56
57
58
def same_type_destructure(target_match_dict, value_match_dict):
59
    for (target_key, _), (value_key, value_value) in zip(target_match_dict, value_match_dict):
60
        if target_key != value_key:
61
            raise MatchFailure
62
        yield value_value
63
64
65
class AttrPatternDestructurer(Destructurer):
66
67
    def destructure(self, value):
68
        if isinstance(value, AttrPattern):
69
            if len(value.match_dict) < len(self.target.match_dict):
70
                raise MatchFailure
71
            return reversed(list(same_type_destructure(self.target.match_dict, value.match_dict)))
72
        return key_destructure(value, self.target.match_dict, guarded_getattr)
73
74
    type = AttrPattern
75
76
77
def guarded_getitem(value, target_key):
78
    try:
79
        return value[target_key]
80
    except KeyError:
81
        raise MatchFailure
82
83
84
class DictPatternDestructurer(Destructurer):
85
86
    def destructure(self, value):
87
        if isinstance(value, DictPattern):
88
            if len(value.match_dict) < len(self.target.match_dict):
89
                raise MatchFailure
90
            if self.target.exhaustive and len(value.match_dict) > len(self.target.match_dict):
91
                raise MatchFailure
92
            return reversed(list(same_type_destructure(self.target.match_dict, value.match_dict)))
93
        if self.target.exhaustive and len(value) != len(self.target.match_dict):
94
            raise MatchFailure
95
        return key_destructure(value, self.target.match_dict, guarded_getitem)
96
97
    type = DictPattern
98
99
100
class TupleDestructurer(Destructurer):
101
102
    def destructure(self, value):
103
        if isinstance(value, self.target.__class__) and len(self.target) == len(value):
104
            return reversed(value)
105
        raise MatchFailure
106
107
    type = tuple
108
109
110
class DestructurerList(tuple):
111
112
    __slots__ = ()
113
114
    def __new__(cls, *destructurers):
115
        return super().__new__(cls, destructurers)
116
117
    def get_destructurer(self, item):
118
        for destructurer in self:
119
            if isinstance(item, destructurer.type):
120
                return destructurer(item)
121
        return None
122
123
    @classmethod
124
    def custom(cls, *destructurers):
125
        return cls(
126
            AsPatternDestructurer, ADTDestructurer, AttrPatternDestructurer, DictPatternDestructurer, *destructurers, TupleDestructurer)
127
128
    def names(self, target):
129
        name_list = []
130
        names_seen = set()
131
        to_process = [target]
132
        while to_process:
133
            item = to_process.pop()
134
            if isinstance(item, Pattern):
135
                not_in(names_seen, item.name)
136
                names_seen.add(item.name)
137
                name_list.append(item.name)
138
            else:
139
                destructurer = self.get_destructurer(item)
140
                if destructurer:
141
                    to_process.extend(destructurer(item))
142
        return name_list
143
144
145
DESTRUCTURERS = DestructurerList.custom()
146