Passed
Push — master ( 44e4b2...f4d14a )
by Max
02:39
created

structured_data._destructure   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 82
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 58
dl 0
loc 82
rs 10
c 0
b 0
f 0
wmc 19

10 Methods

Rating   Name   Duplication   Size   Complexity  
A DestructurerList.__new__() 0 2 1
A TupleDestructurer.destructure() 0 4 3
A DestructurerList.names() 0 13 4
A DestructurerList.custom() 0 3 1
A DestructurerList.get_destructurer() 0 9 4
A Destructurer.__call__() 0 2 1
A ADTDestructurer.destructure() 0 4 2
A Destructurer.__init__() 0 2 1
A Destructurer.destructure() 0 2 1
A Destructurer.__init_subclass__() 0 3 1
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 names(self, target) -> typing.List[str]:
67
        name_list: typing.List[str] = []
68
        to_process = [target]
69
        while to_process:
70
            item = to_process.pop()
71
            if isinstance(item, Pattern):
72
                not_in(name_list, item.name)
73
                name_list.append(item.name)
74
            else:
75
                destructurer = self.get_destructurer(item)
76
                if destructurer:
77
                    to_process.extend(destructurer(item))
78
        return name_list
79
80
81
DESTRUCTURERS = DestructurerList.custom()
82