structured_data._match.match_dict   A
last analyzed

Complexity

Total Complexity 20

Size/Duplication

Total Lines 91
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 20
eloc 58
dl 0
loc 91
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A MatchDict.__iter__() 0 2 1
A MatchDict.__init__() 0 2 1
A MatchDict.__len__() 0 2 1
A MatchDict.__delitem__() 0 2 1
A MatchDict.__getitem__() 0 5 2
A MatchDict.__setitem__() 0 5 2

4 Functions

Rating   Name   Duplication   Size   Complexity  
A match() 0 9 2
A _as_name() 0 4 2
A _multi_index() 0 6 3
A _stack_iteration() 0 12 5
1
"""A class for holding match data and allowing rich access."""
2
3
from __future__ import annotations
4
5
import collections
6
import typing
7
8
from .. import _not_in
9
from .. import _stack_iter
10
from . import destructure
11
from . import match_failure
12
from .patterns import basic_patterns
13
14
15
def _stack_iteration(item) -> typing.Optional[_stack_iter.Action]:
16
    target, value = item
17
    if target is basic_patterns.DISCARD:
18
        return None
19
    if isinstance(target, basic_patterns.Pattern):
20
        return _stack_iter.Yield(item)
21
    destructurer = destructure.DESTRUCTURERS.get_destructurer(target)
22
    if destructurer:
23
        return _stack_iter.Extend(zip(destructurer(target), destructurer(value)))
24
    if target != value:
25
        raise match_failure.MatchFailure
26
    return None
27
28
29
def match(target, value) -> MatchDict:
30
    """Extract all of the matches between target and value."""
31
    match_dict = MatchDict()
32
    for pattern, local_value in _stack_iter.stack_iter(
33
        (target, value), _stack_iteration
34
    ):
35
        _not_in.not_in(container=match_dict, item=pattern.name)
36
        match_dict[pattern.name] = local_value
37
    return match_dict
38
39
40
def _as_name(key):
41
    if isinstance(key, basic_patterns.Pattern):
42
        return key.name
43
    return key
44
45
46
def _multi_index(dct, key):
47
    if isinstance(key, tuple):
48
        return tuple(dct[sub_key] for sub_key in key)
49
    if isinstance(key, dict):
50
        return {name: dct[value] for (name, value) in key.items()}
51
    raise KeyError(key)
52
53
54
class MatchDict(collections.abc.MutableMapping):
55
    """A MutableMapping that allows for retrieval into structures.
56
57
    The actual keys in the mapping must be string values. Most of the mapping
58
    methods will only operate on or yield string keys. The exception is
59
    subscription: the "key" in subscription can be a structure made of tuples
60
    and dicts. For example, ``md["a", "b"] == (md["a"], md["b"])``, and
61
    ``md[{1: "a"}] == {1: md["a"]}``. The typical use of this will be to
62
    extract many match values at once, as in ``a, b, c == md["a", "b", "c"]``.
63
64
    The behavior of most of the pre-defined MutableMapping methods is currently
65
    neither tested nor guaranteed.
66
    """
67
68
    def __init__(self) -> None:
69
        self.data: typing.Dict[str, typing.Any] = {}
70
71
    def __getitem__(self, key):
72
        key = _as_name(key)
73
        if isinstance(key, str):
74
            return self.data[key]
75
        return _multi_index(self, key)
76
77
    def __setitem__(self, key, value):
78
        key = _as_name(key)
79
        if not isinstance(key, str):
80
            raise TypeError
81
        self.data[key] = value
82
83
    def __delitem__(self, key):
84
        del self.data[_as_name(key)]
85
86
    def __iter__(self) -> typing.Iterator[str]:
87
        yield from self.data
88
89
    def __len__(self) -> int:
90
        return len(self.data)
91