11from decimal import Decimal
22from types import GeneratorType
3- from typing import Any , Union , Generator , Iterable , Callable , Final
3+ from typing import Any , Union , Generator , Iterable , Callable , Final , TYPE_CHECKING
44
55
66class SafeString :
@@ -128,38 +128,43 @@ def __init__(self, name: str, self_closing: bool = False) -> None:
128128
129129 def __call__ (
130130 self ,
131- attrs_or_first_child : Union [dict [Union [SafeString , str ], Union [str , SafeString , None ]], Node ],
131+ attrs_or_first_child : Union [dict [Union [SafeString , str ], Union [str , SafeString , int , float , Decimal , None ]], Node ],
132132 * children : Node ,
133133 ) -> Union [TagTuple , SafeString ]:
134134 if isinstance (attrs_or_first_child , dict ):
135135 # in this case this tends to be faster than attrs = "".join([...])
136- tag_start_with_attrs = self . tag_start
136+ attrs : list [ str ] = []
137137 for key in attrs_or_first_child :
138138 # seems to be faster than using .items()
139- val : Union [str , SafeString , None ] = attrs_or_first_child [key ]
139+ val : Union [str , SafeString , int , float , Decimal , None ] = attrs_or_first_child [key ]
140140
141141 # optimization: a large portion of attribute keys should be
142142 # covered by this check. It allows us to skip escaping
143143 # where it is not needed. Note this is for attribute names only;
144144 # attributes values are always escaped (when they are `str`s)
145+ # key_: str
145146 if key not in _common_safe_attribute_names :
146147 key = (
147148 escape_attribute_key (key )
148149 if isinstance (key , str )
149150 else key .safe_str
150151 )
152+ elif TYPE_CHECKING :
153+ assert isinstance (key , str )
151154
152155 if type (val ) is str :
153- tag_start_with_attrs += f' { key } ="{ faster_escape (val )} "'
156+ attrs . append ( f' { key } ="{ faster_escape (val )} "' )
154157 elif type (val ) is SafeString :
155- tag_start_with_attrs += f' { key } ="{ val .safe_str } "'
158+ attrs . append ( f' { key } ="{ val .safe_str } "' )
156159 elif val is None :
157- tag_start_with_attrs += f" { key } "
160+ attrs .append (" " + key )
161+ elif isinstance (val , (int , float , Decimal )):
162+ attrs .append (f' { key } ="{ val } "' )
158163
159164 if children :
160- return f" { tag_start_with_attrs } >" , children , self .closing_tag
165+ return self . tag_start + "" . join ( attrs ) + " >" , children , self .closing_tag
161166 else :
162- return SafeString (f" { tag_start_with_attrs } { self .no_children_close } " )
167+ return SafeString (self . tag_start + "" . join ( attrs ) + self .no_children_close )
163168 else :
164169 return self .tag_start_no_attrs , (attrs_or_first_child ,) + children , self .closing_tag
165170
@@ -407,7 +412,8 @@ def _render(nodes: Iterable[Node], append_to_list: Callable[[str], None]) -> Non
407412def render_styles (
408413 styles : dict [Union [str , SafeString ], Union [str , int , float , Decimal , SafeString ]]
409414) -> SafeString :
410- ret = ""
415+ ret : list [str ] = []
416+ app = ret .append
411417 for k , v in styles .items ():
412418 if k not in _common_safe_css_props :
413419 if isinstance (k , SafeString ):
@@ -421,9 +427,9 @@ def render_styles(
421427 v = faster_escape (v )
422428 # note that ints and floats pass through these condition checks
423429
424- ret += f"{ k } :{ v } ;"
430+ app ( f"{ k } :{ v } ;" )
425431
426- return SafeString (ret )
432+ return SafeString ("" . join ( ret ) )
427433
428434
429435def render (* nodes : Node ) -> str :
0 commit comments