structured_data._match.patterns.basic_patterns   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 90
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 14
eloc 43
dl 0
loc 90
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A Pattern.name() 0 4 1
A Pattern.__getitem__() 0 2 1
A Pattern.__new__() 0 8 4
A AsPattern.__new__() 0 2 1
A AsPattern.destructure() 0 21 3
A AsPattern.bind() 0 6 2
A AsPattern.pattern() 0 4 1
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) -> 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
        """Bind the given pattern and structure, if possible."""
55
        if structure is DISCARD:
56
            return pattern
57
        return cls(pattern, structure)
58
59
    @property
60
    def pattern(self) -> Pattern:
61
        """Return the left-hand-side of the as-match."""
62
        return self[0]
63
64
    @property
65
    def structure(self):
66
        """Return the right-hand-side of the as-match."""
67
        return self[1]
68
69
    def destructure(self, value):
70
        """Return a tuple of sub-values to check.
71
72
        By default, return the value twice.
73
74
        If ``isinstance(value, AsPattern)``, return two values:
75
        First, the structure to match against.
76
        Second, by default, the value itself, but if ``value is self``,
77
        instead return the pattern to bind to.
78
79
        The behavior of returning the full AsPattern when ``value is not self``
80
        is present because it makes it possible to sensically use an AsPattern
81
        as a target, which I don't know if there's a good reason for, but it
82
        was easy to implement.
83
        """
84
        if isinstance(value, AsPattern):
85
            # Letting mutmut touch this line thoroughly locked things up.
86
            if value is self:  # pragma: no mutate
87
                return (self.structure, self.pattern)
88
            return (value.structure, value)
89
        return (value, value)
90