phml

Python Hypertext Markup Language (phml)

version License tired-fox - phml stars - phml forks - phml

Python Hypertext Markup Language (phml)

Deploy Docs GitHub release issues - phml quality testing test coverage

TOC

[![view - Documentation](https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge)](https://tired-fox.github.io/phml/phml.html "Go to project documentation")

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![version](assets/badges/version.svg) [![License](https://img.shields.io/badge/License-MIT-9cf)](https://github.com/Tired-Fox/phml/blob/main/LICENSE) [![tired-fox - phml](https://img.shields.io/static/v1?label=tired-fox&message=phml&color=9cf&logo=github)](https://github.com/tired-fox/phml "Go to GitHub repo")
  5[![stars - phml](https://img.shields.io/github/stars/tired-fox/phml?style=social)](https://github.com/tired-fox/phml)
  6[![forks - phml](https://img.shields.io/github/forks/tired-fox/phml?style=social)](https://github.com/tired-fox/phml)
  7
  8# Python Hypertext Markup Language (phml)
  9
 10[![Deploy Docs](https://github.com/Tired-Fox/phml/actions/workflows/deploy_docs.yml/badge.svg)](https://github.com/Tired-Fox/phml/actions/workflows/deploy_docs.yml) [![GitHub release](https://img.shields.io/github/release/tired-fox/phml?include_prereleases=&sort=semver&color=brightgreen)](https://github.com/tired-fox/phml/releases/) 
 11[![issues - phml](https://img.shields.io/github/issues/tired-fox/phml)](https://github.com/tired-fox/phml/issues) ![quality](assets/badges/quality.svg) ![testing](assets/badges/testing.svg) ![test coverage](assets/badges/test_cov.svg)
 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[![view - Documentation](https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge)](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"