Passed
Push — master ( eeed31...8ea497 )
by Max
01:01
created

DestructurerList.destructure()   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 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
import typing
2
3
from ._adt_constructor import ADTConstructor
4
from ._match_failure import MatchFailure
5
from ._not_in import not_in
6
from ._patterns.basic_patterns import Pattern
7
from ._patterns.compound_match import CompoundMatch
8
from ._unpack import unpack
9
10
_TYPE = type
11
12
13
class Destructurer:
14
    def __init_subclass__(cls, type, **kwargs):
15
        super().__init_subclass__(**kwargs)
16
        cls.type: _TYPE = type
17
18
    def __init__(self, target):
19
        self.target = target
20
21
    def __call__(self, value):
22
        return self.destructure(value)
23
24
    def destructure(self, value):
25
        raise NotImplementedError
26
27
28
class ADTDestructurer(Destructurer, type=ADTConstructor):
29
    def destructure(self, value):
30
        if value.__class__ is not self.target.__class__:
31
            raise MatchFailure
32
        return reversed(unpack(value))
33
34
35
class TupleDestructurer(Destructurer, type=tuple):
36
    def destructure(self, value):
37
        if isinstance(value, self.target.__class__) and len(self.target) == len(value):
38
            return reversed(value)
39
        raise MatchFailure
40
41
42
T = typing.TypeVar("T", bound="DestructurerList")
43
44
45
class DestructurerList(tuple):
46
47
    __slots__ = ()
48
49
    def __new__(cls, *destructurers):
50
        return super().__new__(cls, destructurers)
51
52
    def get_destructurer(
53
        self, item
54
    ) -> typing.Optional[typing.Callable[[typing.Any], typing.Sequence[typing.Any]]]:
55
        if isinstance(item, CompoundMatch):
56
            return item.destructure
57
        for destructurer in self:
58
            if isinstance(item, destructurer.type):
59
                return destructurer(item)
60
        return None
61
62
    @classmethod
63
    def custom(cls: typing.Type[T], *destructurers) -> T:
64
        return cls(*destructurers, ADTDestructurer, TupleDestructurer)
65
66
    def destructure(self, item):
67
        destructurer = self.get_destructurer(item)
68
        if destructurer:
69
            yield from destructurer(item)
70
71
    def stack_iter(self, target):
72
        to_process = [target]
73
        while to_process:
74
            item = to_process.pop()
75
            if isinstance(item, Pattern):
76
                yield item
77
            else:
78
                to_process.extend(self.destructure(item))
79
80
    def names(self, target) -> typing.List[str]:
81
        name_list: typing.List[str] = []
82
        for item in self.stack_iter(target):
83
            not_in(name_list, item.name)
84
            name_list.append(item.name)
85
        return name_list
86
87
88
DESTRUCTURERS = DestructurerList.custom()
89