phml.builder

phml.utils.builder

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

  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, 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, 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 selector.split("\n"))
 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    if selector is not None:
 70        return parse_node(selector, props, children)
 71
 72    return parse_root(children)
 73
 74
 75def parse_root(children: list):
 76    """From the given information return a built root node."""
 77
 78    node = Root()
 79    __process_children(node, children)
 80
 81
 82def parse_node(selector: str, props: dict, children: list):
 83    """From the provided selector, props, and children build an element node."""
 84    from phml.utils import parse_specifiers  # pylint: disable=import-outside-toplevel
 85
 86    node = parse_specifiers(selector)
 87    if len(node) > 1:
 88        raise Exception("Selector can not be a complex selector")
 89    if not isinstance(node[0], dict) or len(node[0]["attributes"]) > 0:
 90        raise EncodingWarning("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() == "text":
103        return Text(" ".join([child for child in children if isinstance(child, str)]))
104
105    properties = {}
106    for prop in props:
107        properties.update(prop)
108
109    if len(node["classList"]) > 0:
110        properties["class"] = properties["class"] or ""
111        properties["class"] += " ".join(node["classList"])
112    if node["id"] is not None:
113        properties["id"] = node["id"]
114
115    node = Element(
116        node["tag"],
117        properties=properties,
118        startend=len(children) == 0,
119    )
120
121    __process_children(node, children)
122    return node
def p( selector: Optional[str] = None, *args: str | list | int | phml.nodes.root.Root | phml.nodes.element.Element | phml.nodes.text.Text | phml.nodes.comment.Comment | phml.nodes.doctype.DocType | phml.nodes.parent.Parent | phml.nodes.node.Node | phml.nodes.literal.Literal):
41def p(  # pylint: disable=[invalid-name,keyword-arg-before-vararg]
42    selector: Optional[str] = None,
43    *args: str | list | int | All_Nodes,
44):
45    """Generic factory for creating phml nodes."""
46
47    # Get all children | non dict objects
48    children = [child for child in args if isinstance(child, (str, list, int, All_Nodes))]
49
50    # Get all properties | dict objects
51    props = [prop for prop in args if isinstance(prop, dict)]
52
53    if selector is not None:
54        # Is a comment
55        if isinstance(selector, str) and selector.startswith("<!--"):
56            return Comment(selector.replace("<!--", "").replace("-->", ""))
57
58        # Is a text node
59        if (
60            isinstance(selector, str)
61            and (len(selector.split(" ")) > 1 or selector.split("\n"))
62            and len(args) == 0
63        ):
64            return Text(selector)
65
66        if not isinstance(selector, str):
67            args = [selector, *args]
68            selector = None
69
70    if selector is not None:
71        return parse_node(selector, props, children)
72
73    return parse_root(children)

Generic factory for creating phml nodes.