Source code for structured_data._match.match_dict
"""A class for holding match data and allowing rich access."""
from __future__ import annotations
import collections
import typing
from .. import _not_in
from .. import _stack_iter
from . import destructure
from . import match_failure
from .patterns import basic_patterns
def _stack_iteration(item) -> typing.Optional[_stack_iter.Action]:
target, value = item
if target is basic_patterns.DISCARD:
return None
if isinstance(target, basic_patterns.Pattern):
return _stack_iter.Yield(item)
destructurer = destructure.DESTRUCTURERS.get_destructurer(target)
if destructurer:
return _stack_iter.Extend(zip(destructurer(target), destructurer(value)))
if target != value:
raise match_failure.MatchFailure
return None
def match(target, value) -> MatchDict:
"""Extract all of the matches between target and value."""
match_dict = MatchDict()
for pattern, local_value in _stack_iter.stack_iter(
(target, value), _stack_iteration
):
_not_in.not_in(container=match_dict, item=pattern.name)
match_dict[pattern.name] = local_value
return match_dict
def _as_name(key):
if isinstance(key, basic_patterns.Pattern):
return key.name
return key
def _multi_index(dct, key):
if isinstance(key, tuple):
return tuple(dct[sub_key] for sub_key in key)
if isinstance(key, dict):
return {name: dct[value] for (name, value) in key.items()}
raise KeyError(key)
[docs]class MatchDict(collections.abc.MutableMapping):
"""A MutableMapping that allows for retrieval into structures.
The actual keys in the mapping must be string values. Most of the mapping
methods will only operate on or yield string keys. The exception is
subscription: the "key" in subscription can be a structure made of tuples
and dicts. For example, ``md["a", "b"] == (md["a"], md["b"])``, and
``md[{1: "a"}] == {1: md["a"]}``. The typical use of this will be to
extract many match values at once, as in ``a, b, c == md["a", "b", "c"]``.
The behavior of most of the pre-defined MutableMapping methods is currently
neither tested nor guaranteed.
"""
def __init__(self) -> None:
self.data: typing.Dict[str, typing.Any] = {}
def __getitem__(self, key):
key = _as_name(key)
if isinstance(key, str):
return self.data[key]
return _multi_index(self, key)
def __setitem__(self, key, value):
key = _as_name(key)
if not isinstance(key, str):
raise TypeError
self.data[key] = value
def __delitem__(self, key):
del self.data[_as_name(key)]
def __iter__(self) -> typing.Iterator[str]:
yield from self.data
def __len__(self) -> int:
return len(self.data)