phml.builder
phml.utilities.builder
This module serves as a utility to make building elements and ast's easier.
1"""phml.utilities.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 Literal as Lit 9from typing import overload 10 11from phml.nodes import AST, Element, Literal, LiteralType, Node, Parent 12 13__all__ = ["p"] 14 15 16def __process_children(node, children: list[str | list | int | Node]): 17 for child in children: 18 if isinstance(child, (str, float, int)): 19 if ( 20 isinstance(child, str) 21 and child.startswith("<!--") 22 and child.endswith("-->") 23 ): 24 child = child.strip() 25 node.append( 26 Literal(LiteralType.Comment, child.lstrip("<!--").rstrip("-->")) 27 ) 28 else: 29 node.append(Literal(LiteralType.Text, str(child))) 30 elif isinstance(child, Node): 31 node.append(child) 32 elif isinstance(child, list): 33 for nested_child in child: 34 if isinstance(nested_child, (str, float, int)): 35 if ( 36 isinstance(nested_child, str) 37 and nested_child.startswith("<!--") 38 and nested_child.endswith("-->") 39 ): 40 nested_child = nested_child.strip() 41 node.append( 42 Literal( 43 LiteralType.Comment, 44 nested_child.lstrip("<!--").rstrip("-->"), 45 ) 46 ) 47 else: 48 node.append(Literal(LiteralType.Text, str(nested_child))) 49 elif isinstance(nested_child, Node): 50 node.append(nested_child) 51 else: 52 raise TypeError( 53 f"Unkown type <{type(nested_child).__name__}> in {child}:\ 54 {nested_child}", 55 ) 56 57 58@overload 59def p(selector: Node | None = None, *args: str | list | dict | int | Node) -> AST: 60 ... 61 62 63@overload 64def p(selector: str, *args: str | list | dict | int | Node) -> Element: 65 ... 66 67 68@overload 69def p(selector: Lit["text", "comment"], *args: str) -> Literal: 70 ... 71 72 73def p( # pylint: disable=[invalid-name,keyword-arg-before-vararg] 74 selector: str | Node | None = None, 75 *args: str | list | dict | int | Node | None, 76) -> Node | AST | Parent: 77 """Generic factory for creating phml nodes.""" 78 79 # Get all children | non dict objects 80 children = [child for child in args if isinstance(child, (str, list, int, Node))] 81 82 # Get all properties | dict objects 83 props = { 84 key: value 85 for prop in args 86 if isinstance(prop, dict) 87 for key, value in prop.items() 88 } 89 90 if selector is not None: 91 # Is a comment 92 # if isinstance(selector, str) and selector.startswith("<!--"): 93 # return Literal(LiteralType.Comment, selector.replace("<!--", "").replace("-->", "")) 94 # Is a text node 95 # if ( 96 # isinstance(selector, str) 97 # and (len(selector.split(" ")) > 1 or len(selector.split("\n")) > 1) 98 # and len(args) == 0 99 # ): 100 # return Literal(LiteralType.Text, selector) 101 if not isinstance(selector, str): 102 args = (selector, *args) 103 selector = None 104 105 children = [ 106 child for child in args if isinstance(child, (str, list, int, Node)) 107 ] 108 return parse_root(children) 109 return parse_node(selector, props, children) 110 111 return parse_root(children) 112 113 114def parse_root(children: list): 115 """From the given information return a built root node.""" 116 117 node = AST() 118 __process_children(node, children) 119 return node 120 121 122def parse_node(selector: str, props: dict, children: list): 123 """From the provided selector, props, and children build an element node.""" 124 from phml.utilities import ( 125 parse_specifiers, # pylint: disable=import-outside-toplevel 126 ) 127 128 node = parse_specifiers(selector) 129 if not isinstance(node[0], dict) or len(node[0]["attributes"]) > 0: 130 raise TypeError("Selector must be of the format `tag?[#id]?[.classes...]?`") 131 132 node = node[0] 133 134 node["tag"] = "div" if node["tag"] == "*" else node["tag"] 135 136 if node["tag"].lower() == "doctype": 137 return Element("doctype", {"html": True}) 138 139 if node["tag"].lower().strip() == "text": 140 return Literal( 141 LiteralType.Text, 142 " ".join( 143 [ 144 str(child) 145 for child in children 146 if isinstance(child, (str, int, float)) 147 ] 148 ), 149 ) 150 if node["tag"].lower().strip() == "comment": 151 return Literal( 152 LiteralType.Comment, 153 " ".join( 154 [ 155 str(child) 156 for child in children 157 if isinstance(child, (str, int, float)) 158 ] 159 ), 160 ) 161 162 properties = {**props} 163 164 if len(node["classList"]) > 0: 165 properties["class"] = "" if "class" not in properties else properties["class"] 166 properties["class"] += " ".join(node["classList"]) 167 if node["id"] is not None: 168 properties["id"] = node["id"] 169 170 element = Element( 171 node["tag"], 172 attributes=properties, 173 children=[] if len(children) > 0 else None, 174 ) 175 176 __process_children(element, children) 177 return element
def
p( selector: str | phml.nodes.Node | None = None, *args: str | list | dict | int | phml.nodes.Node | None) -> phml.nodes.Node | phml.nodes.AST | phml.nodes.Parent:
74def p( # pylint: disable=[invalid-name,keyword-arg-before-vararg] 75 selector: str | Node | None = None, 76 *args: str | list | dict | int | Node | None, 77) -> Node | AST | Parent: 78 """Generic factory for creating phml nodes.""" 79 80 # Get all children | non dict objects 81 children = [child for child in args if isinstance(child, (str, list, int, Node))] 82 83 # Get all properties | dict objects 84 props = { 85 key: value 86 for prop in args 87 if isinstance(prop, dict) 88 for key, value in prop.items() 89 } 90 91 if selector is not None: 92 # Is a comment 93 # if isinstance(selector, str) and selector.startswith("<!--"): 94 # return Literal(LiteralType.Comment, selector.replace("<!--", "").replace("-->", "")) 95 # Is a text node 96 # if ( 97 # isinstance(selector, str) 98 # and (len(selector.split(" ")) > 1 or len(selector.split("\n")) > 1) 99 # and len(args) == 0 100 # ): 101 # return Literal(LiteralType.Text, selector) 102 if not isinstance(selector, str): 103 args = (selector, *args) 104 selector = None 105 106 children = [ 107 child for child in args if isinstance(child, (str, list, int, Node)) 108 ] 109 return parse_root(children) 110 return parse_node(selector, props, children) 111 112 return parse_root(children)
Generic factory for creating phml nodes.