mirror of
https://github.com/django-components/django-components.git
synced 2025-07-16 21:15:00 +00:00
feat: add spread operator (#596)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
36b8fcfbe6
commit
d6ec62c6be
4 changed files with 637 additions and 108 deletions
|
@ -1,11 +1,36 @@
|
|||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
|
||||
|
||||
from django.template import Context, TemplateSyntaxError
|
||||
from django.template.base import FilterExpression, Parser
|
||||
|
||||
Expression = Union[FilterExpression]
|
||||
RuntimeKwargsInput = Dict[str, Expression]
|
||||
RuntimeKwargPairsInput = List[Tuple[str, Expression]]
|
||||
RuntimeKwargsInput = Dict[str, Union[Expression, "Operator"]]
|
||||
RuntimeKwargPairsInput = List[Tuple[str, Union[Expression, "Operator"]]]
|
||||
|
||||
|
||||
class Operator(ABC):
|
||||
"""
|
||||
Operator describes something that somehow changes the inputs
|
||||
to template tags (the `{% %}`).
|
||||
|
||||
For example, a SpreadOperator inserts one or more kwargs at the
|
||||
specified location.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def resolve(self, context: Context) -> Any: ... # noqa E704
|
||||
|
||||
|
||||
class SpreadOperator(Operator):
|
||||
"""Operator that inserts one or more kwargs at the specified location."""
|
||||
|
||||
def __init__(self, expr: Expression) -> None:
|
||||
self.expr = expr
|
||||
|
||||
def resolve(self, context: Context) -> Dict[str, Any]:
|
||||
return self.expr.resolve(context)
|
||||
|
||||
|
||||
class RuntimeKwargs:
|
||||
|
@ -24,7 +49,12 @@ class RuntimeKwargPairs:
|
|||
def resolve(self, context: Context) -> List[Tuple[str, Any]]:
|
||||
resolved_kwarg_pairs: List[Tuple[str, Any]] = []
|
||||
for key, kwarg in self.kwarg_pairs:
|
||||
resolved_kwarg_pairs.append((key, kwarg.resolve(context)))
|
||||
if isinstance(kwarg, SpreadOperator):
|
||||
spread_kwargs = kwarg.resolve(context)
|
||||
for spread_key, spread_value in spread_kwargs.items():
|
||||
resolved_kwarg_pairs.append((spread_key, spread_value))
|
||||
else:
|
||||
resolved_kwarg_pairs.append((key, kwarg.resolve(context)))
|
||||
|
||||
return resolved_kwarg_pairs
|
||||
|
||||
|
@ -43,12 +73,19 @@ def safe_resolve_list(context: Context, args: List[Expression]) -> List:
|
|||
|
||||
def safe_resolve_dict(
|
||||
context: Context,
|
||||
kwargs: Dict[str, Expression],
|
||||
kwargs: Dict[str, Union[Expression, "Operator"]],
|
||||
) -> Dict[str, Any]:
|
||||
result = {}
|
||||
|
||||
for key, kwarg in kwargs.items():
|
||||
result[key] = kwarg.resolve(context)
|
||||
# If we've come across a Spread Operator (...), we insert the kwargs from it here
|
||||
if isinstance(kwarg, SpreadOperator):
|
||||
spread_dict = kwarg.resolve(context)
|
||||
if spread_dict is not None:
|
||||
for spreadkey, spreadkwarg in spread_dict.items():
|
||||
result[spreadkey] = spreadkwarg
|
||||
else:
|
||||
result[key] = kwarg.resolve(context)
|
||||
return result
|
||||
|
||||
|
||||
|
@ -72,6 +109,31 @@ def is_aggregate_key(key: str) -> bool:
|
|||
return ":" in key and not key.startswith(":")
|
||||
|
||||
|
||||
def is_spread_operator(value: Any) -> bool:
|
||||
if not isinstance(value, str) or not value:
|
||||
return False
|
||||
|
||||
return value.startswith("...")
|
||||
|
||||
|
||||
# A string that starts with `...1=`, `...29=`, etc.
|
||||
# We convert the spread syntax to this, so Django parses
|
||||
# it as a kwarg, so it remains in the original position.
|
||||
#
|
||||
# So from `...dict`, we make `...1=dict`
|
||||
#
|
||||
# That way it's trivial to merge the kwargs after the spread
|
||||
# operator is replaced with actual values.
|
||||
INTERNAL_SPREAD_OPERATOR_RE = re.compile(r"^\.\.\.\d+=")
|
||||
|
||||
|
||||
def is_internal_spread_operator(value: Any) -> bool:
|
||||
if not isinstance(value, str) or not value:
|
||||
return False
|
||||
|
||||
return bool(INTERNAL_SPREAD_OPERATOR_RE.match(value))
|
||||
|
||||
|
||||
def process_aggregate_kwargs(kwargs: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
This function aggregates "prefixed" kwargs into dicts. "Prefixed" kwargs
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue