phml
Python Hypertext Markup Language (phml)
Python Hypertext Markup Language (phml)
TOC
Overview
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>
or
<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 @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# pylint: disable=line-too-long 2"""Python Hypertext Markup Language (phml) 3 4 [](https://github.com/Tired-Fox/phml/blob/main/LICENSE) [](https://github.com/tired-fox/phml "Go to GitHub repo") 5[](https://github.com/tired-fox/phml) 6[](https://github.com/tired-fox/phml) 7 8# Python Hypertext Markup Language (phml) 9 10[](https://github.com/Tired-Fox/phml/actions/workflows/deploy_docs.yml) [](https://github.com/tired-fox/phml/releases/) 11[](https://github.com/tired-fox/phml/issues)    12 13**TOC** 14- [Python Hypertext Markup Language (phml)](#python-hypertext-markup-language-phml) 15 - [Overview](#overview) 16 - [How to use](#how-to-use) 17 18 19<div align="center"> 20 21[](https://tired-fox.github.io/phml/phml.html "Go to project documentation") 22 23</div> 24 25## Overview 26 27The 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. 28 29Let's start with the new `python` element. Python is a whitespace language. As such phml 30has 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: 31 32```python 33<python> 34 if True: 35 print("Hello World") 36</python> 37``` 38 39This phml python block will adjust the offset so that the python is executed as seen below: 40 41```python 42if True: 43 print("Hello World") 44``` 45 46So now we can write python code, now what? You can define functions and variables 47how you normally would and they are now available to the scope of the entire file. 48Take, for instance, the example from above, the one with `py-src="urls('youtube')"`. 49You can define the `URL` function in the `python` element and it can be accessed in an element. So the code would look like this: 50 51```html 52<python> 53def URL(link: str) -> str: 54 links = { 55 "youtube": "https://youtube.com" 56 } 57 if link in links: 58 return links[link] 59 else: 60 return "" 61</python> 62 63... 64 65<a href="{URL('youtube')}">Youtube</a> 66 67or 68 69<a :href="URL('youtube')">Youtube</a> 70``` 71 72phml 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. 73 74Next 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` 75and it contains `Hello World!`. Now this variable can be used like this, `<p>{ message }</p>`, 76which renders to, `<p>Hello World!</p>`. 77 78> Note: Inline python blocks are only rendered in a Text element or inside an html attribute. 79 80Multiline blocks are a lot like inline python blocks, but they also have some differences. 81You 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. 82 83Conditional rendering with `py-if`, `py-elif`, and `py-else` is an extremely helpful tool in phml. 84`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 85fails. `py-else` requires there to be either a `py-if` or a `py-else` immediately before it. It only 86renders if the previous element's condition fails. If `py-elif` or `py-else` is on an element, but 87the 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. 88 89Other 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: 90 91```html 92<ul> 93 <li @for='i in range(3)'> 94 <p>{i}</p> 95 </li> 96</ul> 97``` 98 99The compiled html will be: 100 101```html 102<ul> 103 <li> 104 <p>1</p> 105 </li> 106 <li> 107 <p>2</p> 108 </li> 109 <li> 110 <p>3</p> 111 </li> 112</ul> 113``` 114 115The `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`. 116 117Python attributes are shortcuts for using inline python blocks in html attributes. Normally, in 118phml, 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 119the 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')"`. 120 121This 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. 122 123> :warning: This language is in early planning and development stages. All forms of feedback are encouraged. 124""" 125# pylint: enable=line-too-long 126 127from pathlib import Path 128from typing import Callable, Optional 129 130from .core import ( 131 AST, 132 PHML, 133 NODE, 134 Compiler, 135 Format, 136 Formats, 137 Parser, 138 ASTRenderer, 139 replace_components, 140 substitute_component, 141 combine_component_elements 142) 143from .utilities import * 144 145__version__ = "1.3.0"