@@ -77,6 +77,8 @@ def _instrument(
7777 """
7878 count = len (strings )
7979
80+ print ("Instrumenting:" , strings , callable_ids ) # DEBUG
81+
8082 callable_placeholders : dict [int , str ] = {}
8183
8284 for i , s in enumerate (strings ):
@@ -105,7 +107,9 @@ def _instrument_and_parse_internal(
105107 The result is cached to avoid re-parsing the same template multiple times.
106108 """
107109 instrumented = _instrument (strings , callable_ids )
108- return parse_html (instrumented )
110+ i_list = list (instrumented )
111+ print ("Instrumented:" , "" .join (i_list )) # DEBUG
112+ return parse_html (i_list )
109113
110114
111115def _callable_id (value : object ) -> int | None :
@@ -185,7 +189,9 @@ def _substitute_style_attr(value: object) -> t.Iterable[tuple[str, str | None]]:
185189 yield ("style" , str (value ))
186190
187191
188- def _substitute_spread_attrs (value : object ) -> t .Iterable [tuple [str , str | None ]]:
192+ def _substitute_spread_attrs (
193+ value : object ,
194+ ) -> t .Iterable [tuple [str , str | t .Callable | None ]]:
189195 """
190196 Substitute a spread attribute based on the interpolated value.
191197
@@ -212,7 +218,7 @@ def _substitute_spread_attrs(value: object) -> t.Iterable[tuple[str, str | None]
212218def _substitute_attr (
213219 key : str ,
214220 value : object ,
215- ) -> t .Iterable [tuple [str , str | None ]]:
221+ ) -> t .Iterable [tuple [str , str | t . Callable | None ]]:
216222 """
217223 Substitute a single attribute based on its key and the interpolated value.
218224
@@ -234,15 +240,17 @@ def _substitute_attr(
234240 yield (key , None )
235241 case False | None :
236242 pass
243+ case _ if callable (value ):
244+ yield (key , value )
237245 case _:
238246 yield (key , str (value ))
239247
240248
241249def _substitute_attrs (
242250 attrs : dict [str , str | None ], interpolations : tuple [Interpolation , ...]
243- ) -> dict [str , str | None ]:
251+ ) -> dict [str , str | t . Callable | None ]:
244252 """Substitute placeholders in attributes based on the corresponding interpolations."""
245- new_attrs : dict [str , str | None ] = {}
253+ new_attrs : dict [str , str | ComponentCallable | None ] = {}
246254 for key , value in attrs .items ():
247255 if value and value .startswith (_PLACEHOLDER_PREFIX ):
248256 index = _placholder_index (value )
@@ -297,27 +305,35 @@ def _node_from_value(value: object) -> Node:
297305 children = [_node_from_value (v ) for v in value ]
298306 return Fragment (children = children )
299307 case HasHTMLDunder ():
308+ # CONSIDER: could we return a lazy Text?
300309 return Text (Markup (value .__html__ ()))
301310 case _:
311+ # CONSIDER: could we return a lazy Text?
302312 return Text (str (value ))
303313
304314
315+ type ComponentReturn = Node | Template | str | HasHTMLDunder
316+ type ComponentCallable = t .Callable [..., ComponentReturn | t .Iterable [ComponentReturn ]]
317+
318+
305319def _invoke_component (
306320 tag : str ,
307- new_attrs : dict [str , str | None ],
321+ new_attrs : dict [str , str | t . Callable | None ],
308322 new_children : list [Node ],
309323 interpolations : tuple [Interpolation , ...],
310324) -> Node :
311325 """Substitute a component invocation based on the corresponding interpolations."""
312326 index = _placholder_index (tag )
313327 interpolation = interpolations [index ]
314328 value = format_interpolation (interpolation )
329+ # TODO: consider use of signature() or other approaches to validation.
315330 if not callable (value ):
316331 raise TypeError (
317332 f"Expected a callable for component invocation, got { type (value ).__name__ } "
318333 )
319334 # Call the component and return the resulting node
320335 result = value (* new_children , ** new_attrs )
336+ print ("RESULTIS:" , result ) # DEBUG
321337 match result :
322338 case Node ():
323339 return result
@@ -348,7 +364,10 @@ def _substitute_node(p_node: Node, interpolations: tuple[Interpolation, ...]) ->
348364 if tag .startswith (_PLACEHOLDER_PREFIX ):
349365 return _invoke_component (tag , new_attrs , new_children , interpolations )
350366 else :
351- return Element (tag = tag , attrs = new_attrs , children = new_children )
367+ final_attrs = {
368+ k : str (v ) if v is not None else None for k , v in new_attrs .items ()
369+ }
370+ return Element (tag = tag , attrs = final_attrs , children = new_children )
352371 case Fragment (children = children ):
353372 new_children = _substitute_and_flatten_children (children , interpolations )
354373 return Fragment (children = new_children )
@@ -367,4 +386,3 @@ def html(template: Template) -> Node:
367386 # where interpolations go.
368387 p_node = _instrument_and_parse (template )
369388 return _substitute_node (p_node , template .interpolations )
370- return _substitute_node (p_node , template .interpolations )
0 commit comments