Source code for structured_data.match
"""Utilities for destructuring values using matchables and match targets.
Given a value to destructure, called ``value``:
- Construct a matchable: ``matchable = Matchable(value)``
- The matchable is initially falsy, but it will become truthy if it is passed a
**match target** that matches ``value``:
``assert matchable(some_pattern_that_matches)`` (Matchable returns itself
from the call, so you can put the calls in an if-elif block, and only make a
given call at most once.)
- When the matchable is truthy, it can be indexed to access bindings created by
the target.
"""
from __future__ import annotations
import inspect
from . import _attribute_constructor
from ._match.descriptor import function as function_
from ._match.descriptor.property_ import Property
from ._match.destructure import names
from ._match.match_dict import MatchDict
from ._match.matchable import Matchable
from ._match.patterns.basic_patterns import Pattern
from ._match.patterns.bind import Bind
from ._match.patterns.mapping_match import AttrPattern
from ._match.patterns.mapping_match import DictPattern
# In lower-case for aesthetics.
pat = _attribute_constructor.AttributeConstructor( # pylint: disable=invalid-name
Pattern
)
def _can_overwrite_kind(parameter):
if parameter.kind is inspect.Parameter.POSITIONAL_ONLY:
raise ValueError("Signature already contains positional-only arguments")
if parameter.kind is not inspect.Parameter.POSITIONAL_OR_KEYWORD:
raise ValueError("Cannot overwrite non-POSITIONAL_OR_KEYWORD kind")
def _make_args_positional(func, positional_until):
signature = inspect.signature(func)
new_parameters = list(signature.parameters.values())
for index, parameter in enumerate(new_parameters[:positional_until]):
_can_overwrite_kind(parameter)
new_parameters[index] = parameter.replace(kind=inspect.Parameter.POSITIONAL_ONLY)
new_signature = signature.replace(parameters=new_parameters)
if new_signature != signature:
func.__signature__ = new_signature
# This wraps a function that, for reasons, can't be called directly by the code
# The function body should probably just be a docstring.
[docs]def function(_func=None, *, positional_until=0):
"""Convert a function to dispatch by value.
The original function is not called when the dispatch function is invoked.
"""
def wrap(func):
_make_args_positional(func, positional_until)
return function_.Function(func)
if _func is None:
return wrap
return wrap(_func)
[docs]def decorate_in_order(*args):
"""Apply decorators in the order they're passed to the function."""
def decorator(func):
for arg in args:
func = arg(func)
return func
return decorator
__all__ = [
"AttrPattern",
"Bind",
"DictPattern",
"MatchDict",
"Matchable",
"Pattern",
"Property",
"decorate_in_order",
"function",
"names",
"pat",
]