Coverage for kTemplate/main.py: 100%
23 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-19 17:08 +0800
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-19 17:08 +0800
1from functools import reduce
2from functools import partial
3from typing import Callable
6def attr2str(key: str, attrs: dict) -> str:
7 """
8 create attribute string of specific key in an html element
10 Examples:
12 Here shows the attribute string output from diff types of attribute dict value
14 - str -> str
15 >>> attr2str(key="x", attrs={"x": "y"})
16 ' x="y"'
18 - empty str -> empty str
19 >>> attr2str(key="x", attrs={"x": ""})
20 ' x=""'
22 - non str truthy -> key itself
23 >>> attr2str(key="x", attrs={"x": True})
24 ' x'
25 >>> attr2str(key="x", attrs={"x": 1})
26 ' x'
28 - non str falsy -> omit
29 >>> attr2str(key="x", attrs={"x": False})
30 ''
31 >>> attr2str(key="x", attrs={"x": None})
32 ''
33 >>> attr2str(key="x", attrs={"x": 0})
34 ''
36 - convert attr underscore to hyphen
37 >>> attr2str(key="data_y", attrs={"data_y": "y"})
38 ' data-y="y"'
40 - convert attr name cls to class
41 >>> attr2str(key="cls", attrs={"cls": "y"})
42 ' class="y"'
44 Note:
45 to work around python naming restriction,
46 the key `cls` will conver to `class`,
47 and underscore `_` will convert to hyphen `-`
49 Args:
50 key (str): attribute key name
51 attrs (dict): attribute key-value pairs of an element
53 Returns:
54 str: attribute portion of an element
55 """
56 attr = "class" if key == "cls" else key.replace("_", "-")
57 val = attrs[key]
59 if isinstance(val, str):
60 return f' {attr}="{val}"'
62 return f" {attr}" if val else ""
65def element(tag: str, content: str | list[str] = None, *args, **kwargs) -> str:
66 """return html element with specific tag and attributes
68 Examples:
70 - void element, content=None (default)
71 >>> element(tag="br")
72 '<br />'
74 - void element w/ attr
75 >>> element(tag="img", src="http://img.url")
76 '<img src="http://img.url" />'
78 - empty string content -> element with end tag but no content
79 >>> element(tag="script", content="", src="url")
80 '<script src="url"></script>'
82 - non-string truthy attrubite -> return attribute key itself
83 >>> element(tag="option", content="a", selected=True)
84 '<option selected>a</option>'
86 - non-string falsy attrubite -> attribute omitted
87 >>> element(tag="option", content="a", selected=False)
88 '<option>a</option>'
90 - var positional args -> return attribute key itself
91 >>> element('option', 'foo', 'selected', value='foo')
92 '<option value="foo" selected>foo</option>'
94 - var positional args, useful in UnoCSS attributify mode
95 >>> element('div', None, 'm-2', 'rounded', 'text-teal-400')
96 '<div m-2 rounded text-teal-400 />'
98 - var positional args + keyword args
99 >>> element('a', 'foo', 'm-2', 'rounded', 'text-teal-400', href='bar')
100 '<a href="bar" m-2 rounded text-teal-400>foo</a>'
102 - element tree
103 >>> element(tag="div", content=element("div", "x"))
104 '<div><div>x</div></div>'
106 - mix text w/ element
107 >>> element(tag="div", content=f'x{element("i", "y")}')
108 '<div>x<i>y</i></div>'
110 - content w/ list of elements -> elements in list are siblings
111 >>> element(
112 ... tag="div",
113 ... content=[element("br"), element("a", content="a link", href="url")]
114 ... )
115 '<div><br /><a href="url">a link</a></div>'
117 Args:
118 tag (str): element tag name
119 content (str | list[str], optional): Defaults to None.
120 text or list of other elements, `None` returns element w/o closing tag
121 args (list[str], optional): names of value-less attributes
122 - eg. `defer`, `selected`
123 - it is also useful for UnoCSS attributify mode
124 kwagrs (dict): key-value pairs of html attributes
125 - if val is str, assign `key="val"`
126 - if key is non-string truthy, assign value-less attribute, eg.
127 - selected=True -> selected
128 - defer=1 -> defer
129 - if key is non-str falsy, the key is omitted
130 - eg. <option selected=
131 Returns:
132 str: html element with specific tag and attributes
133 """
134 args_str = " " + " ".join(args) if args else ""
135 kwarg_str = reduce(lambda cum, key: cum + attr2str(key, kwargs), kwargs, "")
137 # content-less `void` element with self closing tag
138 if content is None:
139 return f"<{tag}{kwarg_str}{args_str} />"
141 inner = "".join(content) if isinstance(content, list) else content
142 return f"<{tag}{kwarg_str}{args_str}>{inner}</{tag}>"
145def create_elements(tags: str) -> list[Callable[..., str]]:
146 """create tagged element functions
148 Notes:
149 This is a higher order function that returns a list of functions
151 Examples:
153 - single element
154 >>> funcs = create_elements("div")
155 >>> [f() for f in funcs]
156 ['<div />']
158 - multiple elements
159 >>> funcs = create_elements("a, br")
160 >>> [f() for f in funcs]
161 ['<a />', '<br />']
163 Args:
164 tags (str): names of functions to be created, comma separated
165 eg. "a, br, div, span"
167 Returns:
168 list[TaggedElement]: list of tagged element functions
169 eg. [a br div span]
170 """
171 op = []
172 for tag in tags.split(","):
173 func = partial(element, tag.strip())
174 func.__doc__ = f"""`{tag}` element function
176Args:
177 content (str | list[str], optional): Defaults to None.
178 text or list of other elements, `None` returns element w/o closing tag
179 args (list[str], optional): names of value-less attributes
180 - eg. `defer`, `selected`
181 - it is also useful for UnoCSS attributify mode
182 kwagrs (dict): key-value pairs of html attributes
183 - if val is str, assign `key="val"`
184 - if key is non-string truthy, assign value-less attribute, eg.
185 - selected=True -> selected
186 - defer=1 -> defer
187 - if key is non-str falsy, the key is omitted
188 - eg. <option selected=
189Returns:
190 str: `{tag}` element string with attributes
191"""
192 op.append(func)
194 return op