Completed
Pull Request — master (#41)
by Max
03:26
created

Pattern.name()   A

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
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) -> typing.Union[Pattern, AsPattern]:
41
        return AsPattern.bind(self, other)
42
43
44
class AsPattern(CompoundMatch, tuple):
45
    """A matcher that contains further bindings."""
46
47
    __slots__ = ()
48
49
    def __new__(cls, pattern: Pattern, structure):
50
        return super().__new__(cls, (pattern, structure))  # type: ignore
51
52
    @classmethod
53
    def bind(cls, pattern: Pattern, structure) -> typing.Union[Pattern, AsPattern]:
54
        if structure is DISCARD:
55
            return pattern
56
        return cls(pattern, structure)
57
58
    @property
59
    def pattern(self) -> Pattern:
60
        """Return the left-hand-side of the as-match."""
61
        return self[0]
62
63
    @property
64
    def structure(self):
65
        """Return the right-hand-side of the as-match."""
66
        return self[1]
67
68
    def destructure(self, value):
69
        """Return a tuple of sub-values to check.
70
71
        By default, return the value twice.
72
73
        If ``isinstance(value, AsPattern)``, return two values:
74
        First, the structure to match against.
75
        Second, by default, the value itself, but if ``value is self``,
76
        instead return the pattern to bind to.
77
78
        The behavior of returning the full AsPattern when ``value is not self``
79
        is present because it makes it possible to sensically use an AsPattern
80
        as a target, which I don't know if there's a good reason for, but it
81
        was easy to implement.
82
        """
83
        if isinstance(value, AsPattern):
84
            # Letting mutmut touch this line thoroughly locked things up.
85
            if value is self:  # pragma: no mutate
86
                return (self.structure, self.pattern)
87
            return (value.structure, value)
88
        return (value, value)
89