refactor: simplify slot intermediate slot types

This commit is contained in:
Juro Oravec 2024-04-15 10:04:27 +02:00
parent 094e05054d
commit 8d3a2ba8db
2 changed files with 32 additions and 33 deletions

View file

@ -2,7 +2,7 @@ import inspect
import os
import sys
from pathlib import Path
from typing import Any, ClassVar, Dict, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Type, Union
from typing import Any, ClassVar, Dict, List, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, Union
from django.core.exceptions import ImproperlyConfigured
from django.forms.widgets import Media, MediaDefiningClass
@ -23,12 +23,12 @@ from django_components.component_registry import AlreadyRegistered, ComponentReg
from django_components.logger import logger
from django_components.middleware import is_dependency_middleware_active
from django_components.slots import (
DefaultFillContent,
ImplicitFillNode,
NamedFillContent,
NamedFillNode,
FillContent,
SlotName,
render_component_template_with_slots,
DEFAULT_SLOT_KEY,
)
from django_components.utils import search
@ -186,7 +186,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
self,
registered_name: Optional[str] = None,
outer_context: Optional[Context] = None,
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]] = (), # type: ignore
fill_content: dict[str, FillContent] = {}, # type: ignore
):
self.registered_name: Optional[str] = registered_name
self.outer_context: Context = outer_context or Context()
@ -280,14 +280,13 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
escape_content: bool = True,
) -> None:
"""Fill component slots outside of template rendering."""
self.fill_content = [
(
slot_name,
self.fill_content = {
slot_name: FillContent(
TextNode(escape(content) if escape_content else content),
None,
)
for (slot_name, content) in slots_data.items()
]
}
class ComponentNode(Node):
@ -299,20 +298,14 @@ class ComponentNode(Node):
context_args: List[FilterExpression],
context_kwargs: Mapping[str, FilterExpression],
isolated_context: bool = False,
fill_nodes: Union[ImplicitFillNode, Iterable[NamedFillNode]] = (),
fill_nodes: Sequence[Union[ImplicitFillNode, NamedFillNode]] = (),
) -> None:
self.name_fexp = name_fexp
self.context_args = context_args or []
self.context_kwargs = context_kwargs or {}
self.isolated_context = isolated_context
self.fill_nodes = fill_nodes
self.nodelist = self._create_nodelist(fill_nodes)
def _create_nodelist(self, fill_nodes: Union[Iterable[Node], ImplicitFillNode]) -> NodeList:
if isinstance(fill_nodes, ImplicitFillNode):
return NodeList([fill_nodes])
else:
return NodeList(fill_nodes)
self.nodelist = NodeList(fill_nodes)
def __repr__(self) -> str:
return "<ComponentNode: {}. Contents: {!r}>".format(
@ -330,16 +323,18 @@ class ComponentNode(Node):
resolved_context_args = [safe_resolve(arg, context) for arg in self.context_args]
resolved_context_kwargs = {key: safe_resolve(kwarg, context) for key, kwarg in self.context_kwargs.items()}
if isinstance(self.fill_nodes, ImplicitFillNode):
fill_content = self.fill_nodes.nodelist
if len(self.fill_nodes) == 1 and isinstance(self.fill_nodes[0], ImplicitFillNode):
fill_content: Dict[str, FillContent] = {
DEFAULT_SLOT_KEY: FillContent(self.fill_nodes[0].nodelist, None)
}
else:
fill_content = []
fill_content = {}
for fill_node in self.fill_nodes:
# Note that outer component context is used to resolve variables in
# fill tag.
resolved_name = fill_node.name_fexp.resolve(context)
resolved_fill_alias = fill_node.resolve_alias(context, resolved_component_name)
fill_content.append((resolved_name, fill_node.nodelist, resolved_fill_alias))
fill_content[resolved_name] = FillContent(fill_node.nodelist, resolved_fill_alias)
component: Component = component_cls(
registered_name=resolved_component_name,

View file

@ -1,6 +1,6 @@
import difflib
import sys
from typing import Dict, Iterable, List, Optional, Set, Tuple, Type, Union
from typing import Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, Type, Union
if sys.version_info[:2] < (3, 9):
from typing import ChainMap
@ -19,16 +19,20 @@ from django.template.exceptions import TemplateSyntaxError
from django.utils.safestring import SafeString, mark_safe
FILLED_SLOTS_CONTENT_CONTEXT_KEY = "_DJANGO_COMPONENTS_FILLED_SLOTS"
DEFAULT_SLOT_KEY = "_DJANGO_COMPONENTS_DEFAULT_SLOT"
# Type aliases
SlotName = str
AliasName = str
DefaultFillContent: TypeAlias = NodeList
NamedFillContent = Tuple[SlotName, NodeList, Optional[AliasName]]
FillContent = Tuple[NodeList, Optional[AliasName]]
class FillContent(NamedTuple):
"""Data passed from component to slot to render that slot"""
nodes: NodeList
alias: Optional[AliasName]
FilledSlotsContext = ChainMap[Tuple[SlotName, Template], FillContent]
@ -103,7 +107,7 @@ class SlotNode(Node, TemplateAwareNodeMixin):
extra_context = {}
try:
slot_fill_content: FillContent = filled_slots_map[(self.name, self.template)]
slot_fill_content = filled_slots_map[(self.name, self.template)]
except KeyError:
if self.is_required:
raise TemplateSyntaxError(
@ -244,7 +248,7 @@ class IfSlotFilledNode(Node):
def parse_slot_fill_nodes_from_component_nodelist(
component_nodelist: NodeList,
ComponentNodeCls: Type[Node],
) -> Union[Iterable[NamedFillNode], ImplicitFillNode]:
) -> Sequence[Union[NamedFillNode, ImplicitFillNode]]:
"""
Given a component body (`django.template.NodeList`), find all slot fills,
whether defined explicitly with `{% fill %}` or implicitly.
@ -263,7 +267,7 @@ def parse_slot_fill_nodes_from_component_nodelist(
Then this function returns the nodes (`django.template.Node`) for `fill "first_fill"`
and `fill "second_fill"`.
"""
fill_nodes: Union[Iterable[NamedFillNode], ImplicitFillNode] = []
fill_nodes: Sequence[Union[NamedFillNode, ImplicitFillNode]] = []
if _block_has_content(component_nodelist):
for parse_fn in (
_try_parse_as_default_fill,
@ -340,7 +344,7 @@ def _block_has_content(nodelist: NodeList) -> bool:
def render_component_template_with_slots(
template: Template,
context: Context,
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]],
fill_content: Dict[str, FillContent],
registered_name: Optional[str],
) -> str:
"""
@ -363,16 +367,16 @@ def render_component_template_with_slots(
def _prepare_component_template_filled_slot_context(
template: Template,
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]],
fill_content: Dict[str, FillContent],
slots_context: Optional[FilledSlotsContext],
registered_name: Optional[str],
) -> FilledSlotsContext:
if isinstance(fill_content, NodeList):
default_fill_content = (fill_content, None)
named_fills_content = {}
if DEFAULT_SLOT_KEY in fill_content:
named_fills_content = fill_content.copy()
default_fill_content = named_fills_content.pop(DEFAULT_SLOT_KEY)
else:
named_fills_content = fill_content
default_fill_content = None
named_fills_content = {name: (nodelist, alias) for name, nodelist, alias in list(fill_content)}
# If value is `None`, then slot is unfilled.
slot_name2fill_content: Dict[SlotName, Optional[FillContent]] = {}