Coverage for kTemplate/main.py: 100%

23 statements  

« 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 

4 

5 

6def attr2str(key: str, attrs: dict) -> str: 

7 """ 

8 create attribute string of specific key in an html element 

9 

10 Examples: 

11 

12 Here shows the attribute string output from diff types of attribute dict value 

13 

14 - str -> str 

15 >>> attr2str(key="x", attrs={"x": "y"}) 

16 ' x="y"' 

17 

18 - empty str -> empty str 

19 >>> attr2str(key="x", attrs={"x": ""}) 

20 ' x=""' 

21 

22 - non str truthy -> key itself 

23 >>> attr2str(key="x", attrs={"x": True}) 

24 ' x' 

25 >>> attr2str(key="x", attrs={"x": 1}) 

26 ' x' 

27 

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 '' 

35 

36 - convert attr underscore to hyphen 

37 >>> attr2str(key="data_y", attrs={"data_y": "y"}) 

38 ' data-y="y"' 

39 

40 - convert attr name cls to class 

41 >>> attr2str(key="cls", attrs={"cls": "y"}) 

42 ' class="y"' 

43 

44 Note: 

45 to work around python naming restriction, 

46 the key `cls` will conver to `class`, 

47 and underscore `_` will convert to hyphen `-` 

48 

49 Args: 

50 key (str): attribute key name 

51 attrs (dict): attribute key-value pairs of an element 

52 

53 Returns: 

54 str: attribute portion of an element 

55 """ 

56 attr = "class" if key == "cls" else key.replace("_", "-") 

57 val = attrs[key] 

58 

59 if isinstance(val, str): 

60 return f' {attr}="{val}"' 

61 

62 return f" {attr}" if val else "" 

63 

64 

65def element(tag: str, content: str | list[str] = None, *args, **kwargs) -> str: 

66 """return html element with specific tag and attributes 

67 

68 Examples: 

69 

70 - void element, content=None (default) 

71 >>> element(tag="br") 

72 '<br />' 

73 

74 - void element w/ attr 

75 >>> element(tag="img", src="http://img.url") 

76 '<img src="http://img.url" />' 

77 

78 - empty string content -> element with end tag but no content 

79 >>> element(tag="script", content="", src="url") 

80 '<script src="url"></script>' 

81 

82 - non-string truthy attrubite -> return attribute key itself 

83 >>> element(tag="option", content="a", selected=True) 

84 '<option selected>a</option>' 

85 

86 - non-string falsy attrubite -> attribute omitted 

87 >>> element(tag="option", content="a", selected=False) 

88 '<option>a</option>' 

89 

90 - var positional args -> return attribute key itself 

91 >>> element('option', 'foo', 'selected', value='foo') 

92 '<option value="foo" selected>foo</option>' 

93 

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 />' 

97 

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>' 

101 

102 - element tree 

103 >>> element(tag="div", content=element("div", "x")) 

104 '<div><div>x</div></div>' 

105 

106 - mix text w/ element 

107 >>> element(tag="div", content=f'x{element("i", "y")}') 

108 '<div>x<i>y</i></div>' 

109 

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>' 

116 

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, "") 

136 

137 # content-less `void` element with self closing tag 

138 if content is None: 

139 return f"<{tag}{kwarg_str}{args_str} />" 

140 

141 inner = "".join(content) if isinstance(content, list) else content 

142 return f"<{tag}{kwarg_str}{args_str}>{inner}</{tag}>" 

143 

144 

145def create_elements(tags: str) -> list[Callable[..., str]]: 

146 """create tagged element functions 

147 

148 Notes: 

149 This is a higher order function that returns a list of functions 

150 

151 Examples: 

152 

153 - single element 

154 >>> funcs = create_elements("div") 

155 >>> [f() for f in funcs] 

156 ['<div />'] 

157 

158 - multiple elements 

159 >>> funcs = create_elements("a, br") 

160 >>> [f() for f in funcs] 

161 ['<a />', '<br />'] 

162 

163 Args: 

164 tags (str): names of functions to be created, comma separated 

165 eg. "a, br, div, span" 

166 

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 

175 

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) 

193 

194 return op