Passed
Push — master ( bcf14c...4a1f1b )
by Max
57s
created

structured_data._match.patterns.basic_patterns   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 87
rs 10
c 0
b 0
f 0
wmc 13

7 Methods

Rating   Name   Duplication   Size   Complexity  
A AsPattern.__new__() 0 6 2
A Pattern.name() 0 4 1
A Pattern.__getitem__() 0 2 1
A AsPattern.destructure() 0 21 3
A AsPattern.pattern() 0 4 1
A Pattern.__new__() 0 8 4
A AsPattern.structure() 0 4 1
1
"""The simplest patterns."""
2
3
from __future__ import annotations
4
5
import keyword
6
import sys
7
import typing
8
9
from .compound_match import CompoundMatch
10
11
DISCARD = object()
12
13
14
class Pattern(tuple):
15
    """A matcher that binds a value to a name.
16
17
    A ``Pattern`` can be indexed with another matcher to produce an
18
    ``AsPattern``. When matched with a value, an ``AsPattern`` both binds the
19
    value to the name, and uses the matcher to match the value, thereby
20
    constraining it to have a particular shape, and possibly introducing
21
    further bindings.
22
    """
23
24
    __slots__ = ()
25
26
    def __new__(cls, name: str):
27
        if name == "_":
28
            return DISCARD
29
        if not name.isidentifier():
30
            raise ValueError
31
        if keyword.iskeyword(name):
32
            raise ValueError
33
        return super().__new__(cls, (sys.intern(name),))  # type: ignore
34
35
    @property
36
    def name(self) -> str:
37
        """Return the name of the matcher."""
38
        return tuple.__getitem__(self, 0)
39
40
    def __getitem__(self, other) -> AsPattern:
41
        return AsPattern(self, other)
42
43
44
class AsPattern(CompoundMatch, tuple):
45
    """A matcher that contains further bindings."""
46
47
    __slots__ = ()
48
49
    def __new__(  # type: ignore
50
        cls, pattern: Pattern, structure
51
    ) -> typing.Union[Pattern, AsPattern]:
52
        if structure is DISCARD:
53
            return pattern
54
        return super().__new__(cls, (pattern, structure))  # type: ignore
55
56
    @property
57
    def pattern(self) -> Pattern:
58
        """Return the left-hand-side of the as-match."""
59
        return self[0]
60
61
    @property
62
    def structure(self):
63
        """Return the right-hand-side of the as-match."""
64
        return self[1]
65
66
    def destructure(self, value):
67
        """Return a tuple of sub-values to check.
68
69
        By default, return the value twice.
70
71
        If ``isinstance(value, AsPattern)``, return two values:
72
        First, the structure to match against.
73
        Second, by default, the value itself, but if ``value is self``,
74
        instead return the pattern to bind to.
75
76
        The behavior of returning the full AsPattern when ``value is not self``
77
        is present because it makes it possible to sensically use an AsPattern
78
        as a target, which I don't know if there's a good reason for, but it
79
        was easy to implement.
80
        """
81
        if isinstance(value, AsPattern):
82
            # Letting mutmut touch this line thoroughly locked things up.
83
            if value is self:  # pragma: no mutate
84
                return (self.structure, self.pattern)
85
            return (value.structure, value)
86
        return (value, value)
87