phml

Python Hypertext Markup Language (phml)

The idea behind the creation of Python in Hypertext Markup Language (phml), is to allow for web page generation with direct access to python. This language pulls directly from frameworks like VueJS. There is conditional rendering, components, python elements, inline/embedded python blocks, and much more. Now let's dive into more about this language.

Let's start with the new python element. Python is a whitespace language. As such phml has the challenge of maintaining the indentation in an appropriate way. With phml, I have made the decision to allow you to have as much leading whitespace as you want as long as the indentation is consistent. This means that indentation is based on the first lines offset. Take this phml example:

<python>
    if True:
        print("Hello World")
</python>

This phml python block will adjust the offset so that the python is executed as seen below:

if True:
    print("Hello World")

So now we can write python code, now what? You can define functions and variables how you normally would and they are now available to the scope of the entire file. Take, for instance, the example from above, the one with py-src="urls('youtube')". You can define the URL function in the python element and it can be accessed in an element. So the code would look like this:

<python>
def URL(link: str) -> str:
    links = {
        "youtube": "https://youtube.com"
    }
    if link in links:
        return links[link]
    else:
        return ""
</python>

...

<a href="{URL('youtube')}">Youtube</a>

phml combines all python elements and treats them as a python file. All local variables and imports are parsed and stored so that they may be accessed later. With that in mind that means you have the full power of the python programming language.

Next up is inline python blocks. These are represented with {}. Any text in-between the brackets will be processed as python. This is mostly useful when you want to inject a value from python. Assume that there is a variable defined in the python element called message and it contains Hello World!. Now this variable can be used like this, <p>{ message }</p>, which renders to, <p>Hello World!</p>.

Note: Inline python blocks are only rendered in a Text element or inside an html attribute.

Multiline blocks are a lot like inline python blocks, but they also have some differences. You can do whatever you like inside this block, however if you expect a value to come from the block you must have at least one local variable. The last local variable defined in this block is used at the result/value.

Conditional Rendering with py-if, py-elif, and py-else is an extremely helpful tool in phml. py-if can be used alone and that the python inside it's value must be truthy for the element to be rendered. py-elif requires an element with a py-if or py-elif attribute immediately before it, and it's condition is rendered the same as py-if but only rendered if a py-if or py-elif first fails. py-else requires there to be either a py-if or a py-else immediately before it. It only renders if the previous element's condition fails. If py-elif or py-else is on an element, but the previous element isn't a py-if or py-elif then an exception will occur. Most importantly, the first element in a chain of conditions must be a py-if. For ease of use, instead of writing py-if, py-elif, or py-else can be written as @if, @elif, or @else respectively.

Other than conditions, there is also a built in py-for attribute. Any element with py-for will take a python for-loop expression that will be applied to that element. So if you did something like this:

<ul>
    <li py-for='i in range(3)'>
        <p>{i}</p>
    </li>
</ul>

The compiled html will be:

<ul>
    <li>
        <p>1</p>
    </li>
    <li>
        <p>2</p>
    </li>
    <li>
        <p>3</p>
    </li>
</ul>

The for and : in the for loops condition are optional. So you can combine for, i in range(10), and : or leave out for and : at your discretion. py-for can also be written as @for.

Python attributes are shortcuts for using inline python blocks in html attributes. Normally, in phml, you would inject python logic into an attribute similar to this: src="{url('youtube')}". If you would like to make the whole attribute value a python expression you may prefix any attribute with a py- or :. This keeps the attribute name the same after the prefix, but tells the parser that the entire value should be processed as python. So the previous example can also be expressed as py-src="URL('youtube')" or :src="URL('youtube')".

This language also has the ability to convert back to html and json with converting to html having more features. Converting to json is just a json representation of a phml ast. However, converting to html is where the magic happens. The compiler executes python blocks, substitutes components, and processes conditions to create a final html string that is dynamic to its original ast. A user may pass additional kwargs to the compiler to expose additional data to the execution of python blocks. If you wish to compile to a non supported language the compiler can take a callable that returns the final string. It passes all the data; components, kwargs, ast, etc… So if a user wishes to extend the language thay may.

:warning: This language is in early planning and development stages. All forms of feedback are encouraged.

  1"""Python Hypertext Markup Language (phml)
  2
  3The idea behind the creation of Python in Hypertext Markup Language (phml), is to allow for web page
  4generation with direct access to python. This language pulls directly from frameworks like VueJS.
  5There is conditional rendering, components, python elements, inline/embedded python blocks, and much
  6more. Now let's dive into more about this language.
  7
  8Let's start with the new `python` element. Python is a whitespace language. As such phml
  9has the challenge of maintaining the indentation in an appropriate way. With phml, I have made the
 10decision to allow you to have as much leading whitespace as you want as long as the indentation is
 11consistent. This means that indentation is based on the first lines offset. Take this phml example:
 12
 13```python
 14<python>
 15    if True:
 16        print("Hello World")
 17</python>
 18```
 19
 20This phml python block will adjust the offset so that the python is executed as seen below:
 21
 22```python
 23if True:
 24    print("Hello World")
 25```
 26
 27So now we can write python code, now what? You can define functions and variables
 28how you normally would and they are now available to the scope of the entire file.
 29Take, for instance, the example from above, the one with `py-src="urls('youtube')"`.
 30You can define the `URL` function in the `python` element and it can be accessed in an element. So
 31the code would look like this:
 32
 33```html
 34<python>
 35def URL(link: str) -> str:
 36    links = {
 37        "youtube": "https://youtube.com"
 38    }
 39    if link in links:
 40        return links[link]
 41    else:
 42        return ""
 43</python>
 44
 45...
 46
 47<a href="{URL('youtube')}">Youtube</a>
 48```
 49
 50phml combines all `python` elements and treats them as a python file. All local variables and
 51imports are parsed and stored so that they may be accessed later. With that in mind that means you
 52have the full power of the python programming language.
 53
 54Next up is inline python blocks. These are represented with `{}`. Any text in-between the brackets
 55will be processed as python. This is mostly useful when you want to inject a value from python.
 56Assume that there is a variable defined in the `python` element called `message`
 57and it contains `Hello World!`. Now this variable can be used like this, `<p>{ message }</p>`,
 58which renders to, `<p>Hello World!</p>`.
 59
 60> Note:  Inline python blocks are only rendered in a Text element or inside an html attribute.
 61
 62Multiline blocks are a lot like inline python blocks, but they also have some differences.
 63You can do whatever you like inside this block, however if you expect a value to come from the block
 64you must have at least one local variable. The last local variable defined in this block is used at
 65the result/value.
 66
 67Conditional Rendering with `py-if`, `py-elif`, and `py-else` is an extremely helpful tool in phml.
 68`py-if` can be used alone and that the python inside it's value must be truthy for the element to be
 69rendered. `py-elif` requires an element with a `py-if` or `py-elif` attribute immediately before
 70it, and it's condition is rendered the same as `py-if` but only rendered if a `py-if` or `py-elif`
 71first
 72fails. `py-else` requires there to be either a `py-if` or a `py-else` immediately before it. It only
 73renders if the previous element's condition fails. If `py-elif` or `py-else` is on an element, but
 74the previous element isn't a `py-if` or `py-elif` then an exception will occur. Most importantly,
 75the first element in a chain of conditions must be a `py-if`. For ease of use, instead of writing
 76`py-if`, `py-elif`, or `py-else` can be written as `@if`, `@elif`, or `@else` respectively.
 77
 78Other than conditions, there is also a built in `py-for` attribute. Any element with py-for will
 79take a python for-loop expression that will be applied to that element. So if you did something like
 80this:
 81
 82```html
 83<ul>
 84    <li py-for='i in range(3)'>
 85        <p>{i}</p>
 86    </li>
 87</ul>
 88```
 89
 90The compiled html will be:
 91
 92```html
 93<ul>
 94    <li>
 95        <p>1</p>
 96    </li>
 97    <li>
 98        <p>2</p>
 99    </li>
100    <li>
101        <p>3</p>
102    </li>
103</ul>
104```
105
106The `for` and `:` in the for loops condition are optional. So you can combine `for`,
107`i in range(10)`, and `:` or leave out `for` and `:` at your discretion. `py-for` can also be
108written as `@for`.
109
110Python attributes are shortcuts for using inline python blocks in html attributes. Normally, in
111phml, you would inject python logic into an attribute similar to this: `src="{url('youtube')}"`. If
112you would like to make the whole attribute value a python expression you may prefix any attribute
113with a `py-` or `:`. This keeps the attribute name the same after the prefix, but tells
114the parser that the entire value should be processed as python. So the previous example can also be
115expressed as `py-src="URL('youtube')"` or `:src="URL('youtube')"`.
116
117This language also has the ability to convert back to html and json with converting to html having
118more features. Converting to json is just a json representation of a phml ast. However, converting
119to html is where the magic happens. The compiler executes python blocks, substitutes components, and
120processes conditions to create a final html string that is dynamic to its original ast. A user may
121pass additional kwargs to the compiler to expose additional data to the execution of python blocks.
122If you wish to compile to a non supported language the compiler can take a callable that returns the
123final string. It passes all the data; components, kwargs, ast, etc… So if a user wishes to extend
124the language thay may.
125
126> :warning: This language is in early planning and development stages. All forms of feedback are
127encouraged.
128"""
129
130from pathlib import Path
131from typing import Callable, Optional
132
133from .core import Compiler, Parser, file_types
134from .locate import *
135from .misc import *
136from .nodes import AST, All_Nodes
137from .transform import *
138from .travel import *
139from .validate import *
140
141__version__ = "1.0.0"
142
143
144class PHMLCore:
145    """A helper class that bundles the functionality
146    of the parser and compiler together. Allows for loading source files,
147    parsing strings and dicts, rendering to a different format, and finally
148    writing the results of a render to a file.
149    """
150
151    parser: Parser
152    """Instance of a [Parser][phml.parser.Parser]."""
153    compiler: Compiler
154    """Instance of a [Compiler][phml.compile.Compiler]."""
155    scopes: Optional[list[str]]
156    """List of paths from cwd to auto add to python path. This helps with
157    importing inside of phml files.
158    """
159
160    @property
161    def ast(self) -> AST:
162        """Reference to the parser attributes ast value."""
163        return self.parser.ast
164
165    @ast.setter
166    def ast(self, _ast: AST):
167        self.parser.ast = _ast
168
169    def __init__(
170        self,
171        scopes: Optional[list[str]] = None,
172        components: Optional[dict[str, dict[str, list | All_Nodes]]] = None,
173    ):
174        self.parser = Parser()
175        self.compiler = Compiler(components=components)
176        self.scopes = scopes or []
177
178    def add(
179        self,
180        *components: dict[str, dict[str, list | All_Nodes] | AST]
181        | tuple[str, dict[str, list | All_Nodes] | AST]
182        | Path,
183    ):
184        """Add a component to the element replacement list.
185
186        Components passed in can be of a few types. The first type it can be is a
187        pathlib.Path type. This will allow for automatic parsing of the file at the
188        path and then the filename and parsed ast are passed to the compiler. It can
189        also be a dictionary of str being the name of the element to be replaced.
190        The name can be snake case, camel case, or pascal cased. The value can either
191        be the parsed result of the component from phml.utils.parse_component() or the
192        parsed ast of the component. Lastely, the component can be a tuple. The first
193        value is the name of the element to be replaced; with the second value being
194        either the parsed result of the component or the component's ast.
195
196        Note:
197            Any duplicate components will be replaced.
198
199        Args:
200            components: Any number values indicating
201            name of the component and the the component. The name is used
202            to replace a element with the tag==name.
203        """
204
205        for component in components:
206            if isinstance(component, Path):
207                self.parser.load(component)
208                self.compiler.add((filename_from_path(component), parse_component(self.parser.ast)))
209            elif isinstance(component, dict):
210                self.compiler.add(*list(component.items()))
211        return self
212
213    def remove(self, *components: str | All_Nodes):
214        """Remove an element from the list of element replacements.
215
216        Takes any number of strings or node objects. If a string is passed
217        it is used as the key that will be removed. If a node object is passed
218        it will attempt to find a matching node and remove it.
219        """
220        self.compiler.remove(*components)
221        return self
222
223    def load(self, file_path: str | Path, handler: Optional[Callable] = None):
224        """Load a source files data and parse it to phml.
225
226        Args:
227            file_path (str | Path): The file path to the source file.
228        """
229        self.parser.load(file_path, handler)
230        return self
231
232    def parse(self, data: str | dict, handler: Optional[Callable] = None):
233        """Parse a str or dict object into phml.
234
235        Args:
236            data (str | dict): Object to parse to phml
237        """
238        self.parser.parse(data, handler)
239        return self
240
241    def render(
242        self,
243        file_type: str = file_types.HTML,
244        indent: Optional[int] = None,
245        scopes: Optional[list[str]] = None,
246        **kwargs,
247    ) -> str:
248        """Render the parsed ast to a different format. Defaults to rendering to html.
249
250        Args:
251            file_type (str): The format to render to. Currently support html, phml, and json.
252            indent (Optional[int], optional): The number of spaces per indent. By default it will
253            use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
254            has 2 spaces.
255
256        Returns:
257            str: The rendered content in the appropriate format.
258        """
259
260        scopes = scopes or ["./"]
261        for scope in self.scopes:
262            if scope not in scopes:
263                scopes.append(scope)
264
265        return self.compiler.compile(
266            self.parser.ast,
267            to_format=file_type,
268            indent=indent,
269            scopes=scopes,
270            **kwargs,
271        )
272
273    def write(
274        self,
275        dest: str | Path,
276        file_type: str = file_types.HTML,
277        indent: Optional[int] = None,
278        scopes: Optional[list[str]] = None,
279        **kwargs,
280    ):
281        """Renders the parsed ast to a different format, then writes
282        it to a given file. Defaults to rendering and writing out as html.
283
284        Args:
285            dest (str | Path): The path to the file to be written to.
286            file_type (str): The format to render the ast as.
287            indent (Optional[int], optional): The number of spaces per indent. By default it will
288            use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
289            has 2 spaces.
290            kwargs: Any additional data to pass to the compiler that will be exposed to the
291            phml files.
292        """
293
294        with open(dest, "+w", encoding="utf-8") as dest_file:
295            dest_file.write(
296                self.render(file_type=file_type, indent=indent, scopes=scopes, **kwargs)
297            )
298        return self
class PHMLCore:
145class PHMLCore:
146    """A helper class that bundles the functionality
147    of the parser and compiler together. Allows for loading source files,
148    parsing strings and dicts, rendering to a different format, and finally
149    writing the results of a render to a file.
150    """
151
152    parser: Parser
153    """Instance of a [Parser][phml.parser.Parser]."""
154    compiler: Compiler
155    """Instance of a [Compiler][phml.compile.Compiler]."""
156    scopes: Optional[list[str]]
157    """List of paths from cwd to auto add to python path. This helps with
158    importing inside of phml files.
159    """
160
161    @property
162    def ast(self) -> AST:
163        """Reference to the parser attributes ast value."""
164        return self.parser.ast
165
166    @ast.setter
167    def ast(self, _ast: AST):
168        self.parser.ast = _ast
169
170    def __init__(
171        self,
172        scopes: Optional[list[str]] = None,
173        components: Optional[dict[str, dict[str, list | All_Nodes]]] = None,
174    ):
175        self.parser = Parser()
176        self.compiler = Compiler(components=components)
177        self.scopes = scopes or []
178
179    def add(
180        self,
181        *components: dict[str, dict[str, list | All_Nodes] | AST]
182        | tuple[str, dict[str, list | All_Nodes] | AST]
183        | Path,
184    ):
185        """Add a component to the element replacement list.
186
187        Components passed in can be of a few types. The first type it can be is a
188        pathlib.Path type. This will allow for automatic parsing of the file at the
189        path and then the filename and parsed ast are passed to the compiler. It can
190        also be a dictionary of str being the name of the element to be replaced.
191        The name can be snake case, camel case, or pascal cased. The value can either
192        be the parsed result of the component from phml.utils.parse_component() or the
193        parsed ast of the component. Lastely, the component can be a tuple. The first
194        value is the name of the element to be replaced; with the second value being
195        either the parsed result of the component or the component's ast.
196
197        Note:
198            Any duplicate components will be replaced.
199
200        Args:
201            components: Any number values indicating
202            name of the component and the the component. The name is used
203            to replace a element with the tag==name.
204        """
205
206        for component in components:
207            if isinstance(component, Path):
208                self.parser.load(component)
209                self.compiler.add((filename_from_path(component), parse_component(self.parser.ast)))
210            elif isinstance(component, dict):
211                self.compiler.add(*list(component.items()))
212        return self
213
214    def remove(self, *components: str | All_Nodes):
215        """Remove an element from the list of element replacements.
216
217        Takes any number of strings or node objects. If a string is passed
218        it is used as the key that will be removed. If a node object is passed
219        it will attempt to find a matching node and remove it.
220        """
221        self.compiler.remove(*components)
222        return self
223
224    def load(self, file_path: str | Path, handler: Optional[Callable] = None):
225        """Load a source files data and parse it to phml.
226
227        Args:
228            file_path (str | Path): The file path to the source file.
229        """
230        self.parser.load(file_path, handler)
231        return self
232
233    def parse(self, data: str | dict, handler: Optional[Callable] = None):
234        """Parse a str or dict object into phml.
235
236        Args:
237            data (str | dict): Object to parse to phml
238        """
239        self.parser.parse(data, handler)
240        return self
241
242    def render(
243        self,
244        file_type: str = file_types.HTML,
245        indent: Optional[int] = None,
246        scopes: Optional[list[str]] = None,
247        **kwargs,
248    ) -> str:
249        """Render the parsed ast to a different format. Defaults to rendering to html.
250
251        Args:
252            file_type (str): The format to render to. Currently support html, phml, and json.
253            indent (Optional[int], optional): The number of spaces per indent. By default it will
254            use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
255            has 2 spaces.
256
257        Returns:
258            str: The rendered content in the appropriate format.
259        """
260
261        scopes = scopes or ["./"]
262        for scope in self.scopes:
263            if scope not in scopes:
264                scopes.append(scope)
265
266        return self.compiler.compile(
267            self.parser.ast,
268            to_format=file_type,
269            indent=indent,
270            scopes=scopes,
271            **kwargs,
272        )
273
274    def write(
275        self,
276        dest: str | Path,
277        file_type: str = file_types.HTML,
278        indent: Optional[int] = None,
279        scopes: Optional[list[str]] = None,
280        **kwargs,
281    ):
282        """Renders the parsed ast to a different format, then writes
283        it to a given file. Defaults to rendering and writing out as html.
284
285        Args:
286            dest (str | Path): The path to the file to be written to.
287            file_type (str): The format to render the ast as.
288            indent (Optional[int], optional): The number of spaces per indent. By default it will
289            use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
290            has 2 spaces.
291            kwargs: Any additional data to pass to the compiler that will be exposed to the
292            phml files.
293        """
294
295        with open(dest, "+w", encoding="utf-8") as dest_file:
296            dest_file.write(
297                self.render(file_type=file_type, indent=indent, scopes=scopes, **kwargs)
298            )
299        return self

A helper class that bundles the functionality of the parser and compiler together. Allows for loading source files, parsing strings and dicts, rendering to a different format, and finally writing the results of a render to a file.

PHMLCore( scopes: Optional[list[str]] = None, components: Optional[dict[str, dict[str, list | 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]]] = None)
170    def __init__(
171        self,
172        scopes: Optional[list[str]] = None,
173        components: Optional[dict[str, dict[str, list | All_Nodes]]] = None,
174    ):
175        self.parser = Parser()
176        self.compiler = Compiler(components=components)
177        self.scopes = scopes or []
parser: phml.core.parser.Parser

Instance of a [Parser][phml.parser.Parser].

compiler: phml.core.compile.Compiler

Instance of a [Compiler][phml.compile.Compiler].

scopes: Optional[list[str]]

List of paths from cwd to auto add to python path. This helps with importing inside of phml files.

Reference to the parser attributes ast value.

def add( self, *components: dict[str, dict[str, list | 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] | phml.nodes.AST.AST] | tuple[str, dict[str, list | 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] | phml.nodes.AST.AST] | pathlib.Path):
179    def add(
180        self,
181        *components: dict[str, dict[str, list | All_Nodes] | AST]
182        | tuple[str, dict[str, list | All_Nodes] | AST]
183        | Path,
184    ):
185        """Add a component to the element replacement list.
186
187        Components passed in can be of a few types. The first type it can be is a
188        pathlib.Path type. This will allow for automatic parsing of the file at the
189        path and then the filename and parsed ast are passed to the compiler. It can
190        also be a dictionary of str being the name of the element to be replaced.
191        The name can be snake case, camel case, or pascal cased. The value can either
192        be the parsed result of the component from phml.utils.parse_component() or the
193        parsed ast of the component. Lastely, the component can be a tuple. The first
194        value is the name of the element to be replaced; with the second value being
195        either the parsed result of the component or the component's ast.
196
197        Note:
198            Any duplicate components will be replaced.
199
200        Args:
201            components: Any number values indicating
202            name of the component and the the component. The name is used
203            to replace a element with the tag==name.
204        """
205
206        for component in components:
207            if isinstance(component, Path):
208                self.parser.load(component)
209                self.compiler.add((filename_from_path(component), parse_component(self.parser.ast)))
210            elif isinstance(component, dict):
211                self.compiler.add(*list(component.items()))
212        return self

Add a component to the element replacement list.

Components passed in can be of a few types. The first type it can be is a pathlib.Path type. This will allow for automatic parsing of the file at the path and then the filename and parsed ast are passed to the compiler. It can also be a dictionary of str being the name of the element to be replaced. The name can be snake case, camel case, or pascal cased. The value can either be the parsed result of the component from phml.utils.parse_component() or the parsed ast of the component. Lastely, the component can be a tuple. The first value is the name of the element to be replaced; with the second value being either the parsed result of the component or the component's ast.

Note:

Any duplicate components will be replaced.

Arguments:
  • components: Any number values indicating
  • name of the component and the the component. The name is used
  • to replace a element with the tag==name.
def remove( self, *components: str | 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):
214    def remove(self, *components: str | All_Nodes):
215        """Remove an element from the list of element replacements.
216
217        Takes any number of strings or node objects. If a string is passed
218        it is used as the key that will be removed. If a node object is passed
219        it will attempt to find a matching node and remove it.
220        """
221        self.compiler.remove(*components)
222        return self

Remove an element from the list of element replacements.

Takes any number of strings or node objects. If a string is passed it is used as the key that will be removed. If a node object is passed it will attempt to find a matching node and remove it.

def load( self, file_path: str | pathlib.Path, handler: Optional[Callable] = None):
224    def load(self, file_path: str | Path, handler: Optional[Callable] = None):
225        """Load a source files data and parse it to phml.
226
227        Args:
228            file_path (str | Path): The file path to the source file.
229        """
230        self.parser.load(file_path, handler)
231        return self

Load a source files data and parse it to phml.

Arguments:
  • file_path (str | Path): The file path to the source file.
def parse(self, data: str | dict, handler: Optional[Callable] = None):
233    def parse(self, data: str | dict, handler: Optional[Callable] = None):
234        """Parse a str or dict object into phml.
235
236        Args:
237            data (str | dict): Object to parse to phml
238        """
239        self.parser.parse(data, handler)
240        return self

Parse a str or dict object into phml.

Arguments:
  • data (str | dict): Object to parse to phml
def render( self, file_type: str = 'html', indent: Optional[int] = None, scopes: Optional[list[str]] = None, **kwargs) -> str:
242    def render(
243        self,
244        file_type: str = file_types.HTML,
245        indent: Optional[int] = None,
246        scopes: Optional[list[str]] = None,
247        **kwargs,
248    ) -> str:
249        """Render the parsed ast to a different format. Defaults to rendering to html.
250
251        Args:
252            file_type (str): The format to render to. Currently support html, phml, and json.
253            indent (Optional[int], optional): The number of spaces per indent. By default it will
254            use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
255            has 2 spaces.
256
257        Returns:
258            str: The rendered content in the appropriate format.
259        """
260
261        scopes = scopes or ["./"]
262        for scope in self.scopes:
263            if scope not in scopes:
264                scopes.append(scope)
265
266        return self.compiler.compile(
267            self.parser.ast,
268            to_format=file_type,
269            indent=indent,
270            scopes=scopes,
271            **kwargs,
272        )

Render the parsed ast to a different format. Defaults to rendering to html.

Arguments:
  • file_type (str): The format to render to. Currently support html, phml, and json.
  • indent (Optional[int], optional): The number of spaces per indent. By default it will
  • use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
  • has 2 spaces.
Returns:

str: The rendered content in the appropriate format.

def write( self, dest: str | pathlib.Path, file_type: str = 'html', indent: Optional[int] = None, scopes: Optional[list[str]] = None, **kwargs):
274    def write(
275        self,
276        dest: str | Path,
277        file_type: str = file_types.HTML,
278        indent: Optional[int] = None,
279        scopes: Optional[list[str]] = None,
280        **kwargs,
281    ):
282        """Renders the parsed ast to a different format, then writes
283        it to a given file. Defaults to rendering and writing out as html.
284
285        Args:
286            dest (str | Path): The path to the file to be written to.
287            file_type (str): The format to render the ast as.
288            indent (Optional[int], optional): The number of spaces per indent. By default it will
289            use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
290            has 2 spaces.
291            kwargs: Any additional data to pass to the compiler that will be exposed to the
292            phml files.
293        """
294
295        with open(dest, "+w", encoding="utf-8") as dest_file:
296            dest_file.write(
297                self.render(file_type=file_type, indent=indent, scopes=scopes, **kwargs)
298            )
299        return self

Renders the parsed ast to a different format, then writes it to a given file. Defaults to rendering and writing out as html.

Arguments:
  • dest (str | Path): The path to the file to be written to.
  • file_type (str): The format to render the ast as.
  • indent (Optional[int], optional): The number of spaces per indent. By default it will
  • use the standard for the given format. HTML has 4 spaces, phml has 4 spaces, and json
  • has 2 spaces.
  • kwargs: Any additional data to pass to the compiler that will be exposed to the
  • phml files.