Coverage for phml\builder.py: 99%

69 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-12-08 13:50 -0600

1"""phml.utils.builder 

2 

3This module serves as a utility to make building elements and ast's easier. 

4""" 

5 

6from __future__ import annotations 

7 

8from typing import Optional 

9 

10from phml.nodes import All_Nodes, Comment, DocType, Element, Root, Text 

11 

12__all__ = ["p"] 

13 

14 

15def __process_children(node, children: list[str | list | int | All_Nodes]): 

16 for child in children: 

17 if isinstance(child, str): 

18 node.children.append(Text(child, node)) 

19 elif isinstance(child, (float, int)): 

20 node.children.append(Text(str(child), node)) 

21 elif isinstance(child, All_Nodes): 

22 child.parent = node 

23 node.children.append(child) 

24 elif isinstance(child, list): 

25 for nested_child in child: 

26 if isinstance(nested_child, str): 

27 node.children.append(Text(nested_child, node)) 

28 elif isinstance(nested_child, (float, int)): 

29 node.children.append(Text(str(nested_child), node)) 

30 elif isinstance(nested_child, All_Nodes): 

31 nested_child.parent = node 

32 node.children.append(nested_child) 

33 else: 

34 raise TypeError( 

35 f"Unkown type <{type(nested_child).__name__}> in {child}:\ 

36 {nested_child}" 

37 ) 

38 

39 

40def p( # pylint: disable=[invalid-name,keyword-arg-before-vararg] 

41 selector: Optional[str] = None, 

42 *args: str | list | int | All_Nodes, 

43): 

44 """Generic factory for creating phml nodes.""" 

45 

46 # Get all children | non dict objects 

47 children = [child for child in args if isinstance(child, (str, list, int, All_Nodes))] 

48 

49 # Get all properties | dict objects 

50 props = [prop for prop in args if isinstance(prop, dict)] 

51 

52 if selector is not None: 

53 # Is a comment 

54 if isinstance(selector, str) and selector.startswith("<!--"): 

55 return Comment(selector.replace("<!--", "").replace("-->", "")) 

56 

57 # Is a text node 

58 if ( 

59 isinstance(selector, str) 

60 and (len(selector.split(" ")) > 1 or len(selector.split("\n")) > 1) 

61 and len(args) == 0 

62 ): 

63 return Text(selector) 

64 

65 if not isinstance(selector, str): 

66 args = [selector, *args] 

67 selector = None 

68 

69 children = [child for child in args if isinstance(child, (str, list, int, All_Nodes))] 

70 return parse_root(children) 

71 return parse_node(selector, props, children) 

72 

73 return parse_root(children) 

74 

75 

76def parse_root(children: list): 

77 """From the given information return a built root node.""" 

78 

79 node = Root() 

80 __process_children(node, children) 

81 return node 

82 

83 

84def parse_node(selector: str, props: dict, children: list): 

85 """From the provided selector, props, and children build an element node.""" 

86 from phml.utils import parse_specifiers # pylint: disable=import-outside-toplevel 

87 

88 node = parse_specifiers(selector) 

89 if not isinstance(node[0], dict) or len(node[0]["attributes"]) > 0: 

90 raise TypeError("Selector must be of the format `tag?[#id]?[.classes...]?`") 

91 

92 node = node[0] 

93 

94 node["tag"] = "div" if node["tag"] == "*" else node["tag"] 

95 

96 if node["tag"].lower() == "doctype": 

97 str_children = [child for child in children if isinstance(child, str)] 

98 if len(str_children) > 0: 

99 return DocType(str_children[0]) 

100 return DocType() 

101 

102 if node["tag"].lower().strip() == "text": 

103 return Text( 

104 " ".join([str(child) for child in children if isinstance(child, (str, int, float))]) 

105 ) 

106 

107 if node["tag"].lower().strip() == "comment": 

108 return Comment( 

109 " ".join([str(child) for child in children if isinstance(child, (str, int, float))]) 

110 ) 

111 

112 properties = {} 

113 for prop in props: 

114 properties.update(prop) 

115 

116 if len(node["classList"]) > 0: 

117 properties["class"] = "" if "class" not in properties else properties["class"] 

118 properties["class"] += " ".join(node["classList"]) 

119 if node["id"] is not None: 

120 properties["id"] = node["id"] 

121 

122 element = Element( 

123 node["tag"], 

124 properties=properties, 

125 startend=len(children) == 0, 

126 ) 

127 

128 __process_children(element, children) 

129 return element