phml.core.formats
A collection of Formats which represent supported file formats. Each format can parse data, either string or dict, into a phml.core.nodes.AST along with compiling a phml.core.nodes.ast into it's corresponding file formats string representation.
1"""phml.core.formats 2 3A collection of Formats which represent supported file formats. Each format can 4parse data, either string or dict, into a phml.core.nodes.AST along with compiling 5a phml.core.nodes.ast into it's corresponding file formats string representation. 6""" 7from __future__ import annotations 8 9from dataclasses import dataclass 10 11from .format import Format 12from .html_format import HTMLFormat 13from .json_format import JSONFormat 14from .phml_format import PHMLFormat 15from .xml_format import XMLFormat 16 17 18@dataclass 19class Formats: 20 """Collection of all built-in file formats.""" 21 22 PHML: Format = PHMLFormat # pylint: disable=invalid-name 23 HTML: Format = HTMLFormat # pylint: disable=invalid-name 24 JSON: Format = JSONFormat # pylint: disable=invalid-name 25 XML: Format = XMLFormat # pylint: disable=invalid-name 26 27 def __iter__(self): 28 return iter(vars(self).values())
@dataclass
class
Formats:
19@dataclass 20class Formats: 21 """Collection of all built-in file formats.""" 22 23 PHML: Format = PHMLFormat # pylint: disable=invalid-name 24 HTML: Format = HTMLFormat # pylint: disable=invalid-name 25 JSON: Format = JSONFormat # pylint: disable=invalid-name 26 XML: Format = XMLFormat # pylint: disable=invalid-name 27 28 def __iter__(self): 29 return iter(vars(self).values())
Collection of all built-in file formats.
Formats( PHML: phml.core.formats.format.Format = <class 'phml.core.formats.phml_format.PHMLFormat'>, HTML: phml.core.formats.format.Format = <class 'phml.core.formats.html_format.HTMLFormat'>, JSON: phml.core.formats.format.Format = <class 'phml.core.formats.json_format.JSONFormat'>, XML: phml.core.formats.format.Format = <class 'phml.core.formats.xml_format.XMLFormat'>)
16class PHMLFormat(Format): 17 """Logic for parsing and compiling html files.""" 18 19 extension: str = "phml" 20 21 @classmethod 22 def parse(cls, data: str, auto_close: bool = True) -> str: 23 return parse_markup(data, cls.__name__, auto_close) 24 25 @classmethod 26 def compile( 27 cls, 28 ast: AST, 29 components: Optional[dict[str, dict[str, list | NODE]]] = None, 30 **kwargs, 31 ) -> AST: 32 """Compile and process the given ast and return the resulting ast.""" 33 return ast 34 35 @classmethod 36 def render( 37 cls, 38 ast: AST, 39 components: Optional[dict[str, dict[str, list | NODE]]] = None, 40 indent: int = 4, 41 **kwargs, 42 ) -> str: 43 indent = indent or 4 44 45 return ASTRenderer(ast, indent).compile()
Logic for parsing and compiling html files.
22class HTMLFormat(Format): 23 """Logic for parsing and compiling html files.""" 24 25 extension: list[str] = ["html", "htm"] 26 27 @classmethod 28 def parse(cls, data: str, auto_close: bool = True) -> str: 29 return parse_markup(data, cls.__name__, auto_close) 30 31 @classmethod 32 def compile( 33 cls, 34 ast: AST, 35 config: Config, 36 components: Optional[dict[str, dict[str, list | NODE]]] = None, 37 **kwargs, 38 ) -> AST: 39 """Compile and process the given ast and return the resulting ast.""" 40 41 components = components or {} 42 src = deepcopy(ast) 43 44 # 1. Search for all python elements and get source info. 45 # - Remove when done 46 virtual_python = VirtualPython() 47 48 for python_block in find_all(src, {"tag": "python"}): 49 if ( 50 len(python_block.children) == 1 51 and python_block.children[0].type == "text" 52 ): 53 virtual_python += VirtualPython( 54 python_block.children[0].normalized(), 55 context={**kwargs} 56 ) 57 58 remove_nodes(src, ["element", {"tag": "python"}]) 59 60 # 2. Replace specific element node with given replacement components 61 # replace_components(src, components, virtual_python, **kwargs) 62 63 # 3. Search each element and find @if, @elif, and @else 64 # - Execute those statements 65 66 apply_conditions(src, config, virtual_python, components, **kwargs) 67 68 for python_block in find_all(src, {"tag": "python"}): 69 if ( 70 len(python_block.children) == 1 71 and python_block.children[0].type == "text" 72 ): 73 virtual_python += VirtualPython( 74 python_block.children[0].normalized(), 75 context={**kwargs} 76 ) 77 78 remove_nodes(src, ["element", {"tag": "python"}]) 79 80 # 4. Search for python blocks and process them. 81 82 apply_python(src, virtual_python, **kwargs) 83 remove_nodes(src, {"tag": "slot"}) 84 85 return src 86 87 @classmethod 88 def render( 89 cls, 90 ast: AST, 91 config: Config, 92 components: Optional[dict[str, dict[str, list | NODE]]] = None, 93 indent: int = 4, 94 **kwargs, 95 ) -> str: 96 indent = indent or 4 97 components = components or {} 98 src = ast 99 100 # 1. Search for all python elements and get source info. 101 # - Remove when done 102 virtual_python = VirtualPython() 103 104 for python_block in find_all(src, {"tag": "python"}): 105 if len(python_block.children) == 1: 106 if python_block.children[0].type == "text": 107 virtual_python += VirtualPython(python_block.children[0].normalized()) 108 109 remove_nodes(src, ["element", {"tag": "python"}]) 110 111 # 2. Replace specific element node with given replacement components 112 # replace_components(src, components, virtual_python, **kwargs) 113 114 # 3. Search each element and find @if, @elif, and @else 115 # - Execute those statements 116 117 apply_conditions(src, config, virtual_python, components, **kwargs) 118 119 for python_block in find_all(src, {"tag": "python"}): 120 if len(python_block.children) == 1: 121 if python_block.children[0].type == "text": 122 virtual_python += VirtualPython(python_block.children[0].normalized()) 123 124 remove_nodes(src, ["element", {"tag": "python"}]) 125 126 # 4. Search for python blocks and process them. 127 128 apply_python(src, virtual_python, **kwargs) 129 remove_nodes(src, {"tag": "slot"}) 130 131 return ASTRenderer(src, indent).compile()
Logic for parsing and compiling html files.
81class JSONFormat(Format): 82 """Logic for parsing and compiling html files.""" 83 84 extension: str = "json" 85 86 @classmethod 87 def parse(cls, data: dict | str) -> str: 88 if isinstance(data, str): 89 data = loads(data) 90 91 if isinstance(data, dict): 92 node = construct_tree(data) 93 if not isinstance(node, Root): 94 node = Root(children=[node]) 95 return AST(node) 96 97 raise Exception("Data passed to JSONFormat.parse must be either a str or a dict") 98 99 @classmethod 100 def render( 101 cls, 102 ast: AST, 103 components: Optional[dict[str, dict[str, list | NODE]]] = None, 104 indent: int = 2, 105 **kwargs, 106 ) -> str: 107 indent = indent or 2 108 109 def compile_children(node: Root | Element) -> dict: 110 data = {"type": node.type} 111 112 if node.type == "root": 113 if node.parent is not None: 114 raise Exception("Root nodes must only occur as the root of an ast/tree") 115 116 for attr in vars(node): 117 if attr not in ["parent", "children", "num_lines"]: 118 value = getattr(node, attr) 119 if isinstance(value, Position): 120 data[attr] = value.as_dict() 121 else: 122 data[attr] = value 123 124 if hasattr(node, "children"): 125 data["children"] = [] 126 for child in visit_children(node): 127 data["children"].append(compile_children(child)) 128 129 return data 130 131 data = compile_children(ast.tree) 132 return dumps(data, indent=indent)
Logic for parsing and compiling html files.
51class XMLFormat(Format): 52 """Logic for parsing and compiling html files.""" 53 54 extension: str = "xml" 55 56 @classmethod 57 def parse(cls, data: str) -> str: 58 if isinstance(data, Path): 59 with open(data, "r", encoding="utf-8") as file: 60 data = file.read() 61 62 if isinstance(data, str): 63 root = fromstring(data) 64 _namespace = namespace(root) 65 root = AST(Root(children=[construct_element(root)])) 66 67 if _namespace != "": 68 root.children[0]["xmlns"] = _namespace 69 70 return root 71 raise Exception("Data passed into XMLFormat.parse must be either str or pathlib.Path") 72 73 @classmethod 74 def compile( 75 cls, 76 ast: AST, 77 components: Optional[dict[str, dict[str, list | NODE]]] = None, 78 **kwargs, 79 ) -> AST: 80 """Compile and process the given ast and return the resulting ast.""" 81 82 attribs = { 83 "version": kwargs.pop("version", None) or "1.0", 84 "encoding": kwargs.pop("encoding", None) or "UTF-8", 85 } 86 87 ast.tree.insert(0, PI("xml", attribs)) 88 89 src = deepcopy(ast) 90 91 # 3. Search each element and find @if, @elif, and @else 92 # - Execute those statements 93 94 apply_conditions(src, VirtualPython(), **kwargs) 95 96 # 4. Search for python blocks and process them. 97 98 apply_python(src, VirtualPython(), **kwargs) 99 remove_nodes(src, {"tag": "slot"}) 100 101 return src 102 103 @classmethod 104 def render( 105 cls, 106 ast: AST, 107 components: Optional[dict[str, dict[str, list | NODE]]] = None, 108 indent: int = 2, 109 **kwargs, 110 ) -> str: 111 indent = indent or 2 112 113 attribs = { 114 "version": kwargs.pop("version", None) or "1.0", 115 "encoding": kwargs.pop("encoding", None) or "UTF-8", 116 } 117 118 ast.tree.insert(0, PI("xml", attribs)) 119 120 src = deepcopy(ast) 121 122 # 3. Search each element and find @if, @elif, and @else 123 # - Execute those statements 124 125 apply_conditions(src, VirtualPython(), components={}, **kwargs) 126 127 # 4. Search for python blocks and process them. 128 129 apply_python(src, VirtualPython(), **kwargs) 130 remove_nodes(src, {"tag": "slot"}) 131 132 return ASTRenderer(src, indent).compile(include_doctype=False)
Logic for parsing and compiling html files.