Skip to content

Commit

Permalink
Merge branch 'main' of github.com:basxsoftwareassociation/htmlgenerator
Browse files Browse the repository at this point in the history
  • Loading branch information
saemideluxe committed Dec 24, 2024
2 parents 0ef7222 + 4b00a6d commit 88510d7
Showing 1 changed file with 36 additions and 51 deletions.
87 changes: 36 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
HTML Generator
==============
# HTML Generator

A python package to generate HTML from a template which is defined through a tree of render-elements.

Why
---
## Why

- **Guaranteed correct markup**: Don't worry about correct markup-structure anymore, never again count closing divs or get upset about a missing slash.
- **Code formatting included** (if you format your Python code): Don't worry what would be the correct and most consistent way of formatting your HTML, never again search desperately for a Django-template-language-formatter for your IDE. (:heart: black).
- **Easily generate and modify any template**: Don't worry about overwritting 3rd-party html templates just to change a single letter anymore, never again cramp your templates with an infinite number of ```{% if feat %}```-statements trying to cover all possible use cases.
- **Code formatting included** (if you format your Python code): Don't worry what would be the correct and most consistent way of formatting your HTML, never again search desperately for a Django-template-language-formatter for your IDE. (:heart: black).
- **Easily generate and modify any template**: Don't worry about overwritting 3rd-party html templates just to change a single letter anymore, never again cramp your templates with an infinite number of `{% if feat %}`-statements trying to cover all possible use cases.

- **Generate HTML in the same manner that you write Python code**: Use functions to generate parameterized html objects or build a custom declarativ-like system to compose output
- **Keep the advantages of lazy rendering**: Render contexts and lazy values allow for implicit dependencies, like traditional templates.
- **Define your own components**: Subclassing the base elements allows for easy implementation of e.g. a custom design or layout system.

For more in depth reasoning and thoughts check out this great articel: https://www.devever.net/~hl/stringtemplates

Getting started
---------------
## Getting started

Installing:

Expand All @@ -28,7 +25,7 @@ A simple example:
```python
import htmlgenerator as hg

my_page = hg.HTML(hg.HEAD(), hg.BODY(hg.H1("It works!")))
my_page = hg.HTML(hg.HEAD(), hg.BODY(hg.H1("It works!")), doctype=True)

print(hg.render(my_page, {}))
```
Expand All @@ -41,11 +38,10 @@ This will print the following HTML-document:

Note that the provided implementation of the HTML tag and the HEAD tag come with sensible defaults for DOCTYPE and META tags.

HTML elements
-------------
## HTML elements

The package ```htmlgenerator``` defines all standard HTML tags with uppercase classnames, e.g. BODY, DIV, SECTION, etc.
The __init__ method of HTML elements will treat all passed arguments as child elements and keyword arguments as attributes on the HTML element. Leading underscores of attribute names will be removed (to allow *class* and *for* attributes to be specified) and underscores will be replaced by dashes because python does not allow identifiers to have a dash and HTML attributes normally do not use underscores.
The package `htmlgenerator` defines all standard HTML tags with uppercase classnames, e.g. BODY, DIV, SECTION, etc.
The **init** method of HTML elements will treat all passed arguments as child elements and keyword arguments as attributes on the HTML element. Leading underscores of attribute names will be removed (to allow _class_ and _for_ attributes to be specified) and underscores will be replaced by dashes because python does not allow identifiers to have a dash and HTML attributes normally do not use underscores.

Example:

Expand Down Expand Up @@ -86,12 +82,10 @@ Output:
<div>My list is:<ol><li>not very long</li><li>having a good time</li><li>rendered with htmlgenerator</li><li>ending with this item</li></ol></div>
```

## Rendering

Rendering
---------

The method ```htmlgenerator.render``` should be used to render an element tree. All nodes in the tree should inherit from ```htmlgenerator.BaseElement```. Leaves in the tree can be arbitrary python objects. The render function expects the root element of the tree and a context dictionary as arguments.
The output is generated by rendering the tree recursively. If an object in the tree is an instance of ```BaseElement``` its render method will be called with the context as single argument and it must return a generator which yields objects of type ```str```. Otherwise the object will be converted to a string and escaped for use in HTML. In order to prevent a string from beeing escaped ```htmlgenerator.mark_safe``` or ```django.utils.html.mark_safe``` from the django package can be used.
The method `htmlgenerator.render` should be used to render an element tree. All nodes in the tree should inherit from `htmlgenerator.BaseElement`. Leaves in the tree can be arbitrary python objects. The render function expects the root element of the tree and a context dictionary as arguments.
The output is generated by rendering the tree recursively. If an object in the tree is an instance of `BaseElement` its render method will be called with the context as single argument and it must return a generator which yields objects of type `str`. Otherwise the object will be converted to a string and escaped for use in HTML. In order to prevent a string from beeing escaped `htmlgenerator.mark_safe` or `django.utils.html.mark_safe` from the django package can be used.

Example python object:

Expand All @@ -118,7 +112,6 @@ Output:
<div>Hello, here is some data: {&#x27;fingers&#x27;: [1, 2, 3, 4, 5], &#x27;stuff&#x27;: {&#x27;set&#x27;: {1, 2, 3}}}</div>
```


Example render object:

```python
Expand Down Expand Up @@ -167,24 +160,22 @@ print(
)
```

Output:
Output:

```hmtl
<div>Hello, here is some HTML: &lt;div style=&quot;font-size: 2rem&quot;&gt;Hello world!&lt;/div&gt;</div>
<div>Hello, here is NOT some HTML: <div style="font-size: 2rem">Hello world!</div></div>
```


Lazy values
-----------
## Lazy values

In order to allow rendering values which are not yet known at construction time but only at render time lazy values can be used.
By default htmlgenerator comes with the following lazy values:

- ```htmlgenerator.ContextFunction```: Calls a function with the context as argument and renders the returned value (shortcut ```htmlgenerator.F```)
- ```htmlgenerator.ContextValue```: Renders a variable from the context, can use . to access nested attributes or dictionary keys (shortcut ```htmlgenerator.C```)
- `htmlgenerator.ContextFunction`: Calls a function with the context as argument and renders the returned value (shortcut `htmlgenerator.F`)
- `htmlgenerator.ContextValue`: Renders a variable from the context, can use . to access nested attributes or dictionary keys (shortcut `htmlgenerator.C`)

A lazy value will be resolved just before it is rendered. Custom implementations of lazy values can be added by inheriting from ```htmlgenerator.Lazy```.
A lazy value will be resolved just before it is rendered. Custom implementations of lazy values can be added by inheriting from `htmlgenerator.Lazy`.

Example:

Expand All @@ -208,16 +199,14 @@ Output:
<div>This text is wrapped inside a div element</div>
```

Virtual elements
----------------
## Virtual elements

In order to allow the building of a dynamic page virtual elements need to be used. The following virtual elements exist:


- ```htmlgenerator.BaseElement```: The base for all elements, can also be used to group elements without generating output by itself
- ```htmlgenerator.If```: Lazy evaluates the first argument at render time and returns the first child on true and the second child on false
- ```htmlgenerator.Iterator```: Takes an iterator which can be a lazy value and renders the child element for each iteration
- ```htmlgenerator.Fragment```: Allows to name a part of the tree which can then selectively be rendered by setting the ```fragment``` parameter when calling ```hg.render``` (inspired by https://htmx.org/essays/template-fragments/)
- `htmlgenerator.BaseElement`: The base for all elements, can also be used to group elements without generating output by itself
- `htmlgenerator.If`: Lazy evaluates the first argument at render time and returns the first child on true and the second child on false
- `htmlgenerator.Iterator`: Takes an iterator which can be a lazy value and renders the child element for each iteration
- `htmlgenerator.Fragment`: Allows to name a part of the tree which can then selectively be rendered by setting the `fragment` parameter when calling `hg.render` (inspired by https://htmx.org/essays/template-fragments/)

Example:

Expand Down Expand Up @@ -247,11 +236,10 @@ It is not cold
<div>Content of my page</div>
```

Converting existing HTML source
-------------------------------
## Converting existing HTML source

htmlgenerator comes with a handy commandline tool to convert existing HTML-code into htmlgenerator python objects.
In order to get all the dependencies for the tool, install it with ```pip install htmlgenerator[all]```
In order to get all the dependencies for the tool, install it with `pip install htmlgenerator[all]`

It can be used with standard input or with a list of files as arguments:

Expand All @@ -261,25 +249,23 @@ It can be used with standard input or with a list of files as arguments:

By default the generated python files are formatted with black.
Python code which has been generated from very large files, e.g. complete websites, might take multiple minutes to be formated.
In order to get unformatted but still valid python code add the flag ```--no-formatting```.
In order to get unformatted but still valid python code add the flag `--no-formatting`.
This will not run black on the generated python code and therefore be very fast.
In order to generate more compact code the flag ```--compact``` can be passed to ```convertfromhtml```.
This will generate the most compact python code and works with and without ```--no-formatting```.
In order to get human readable code the flag ```--compact``` is recommended.
In order to get code fast (especially for big pages) the flag ```--no-formatting``` is recommended.
In order to get a one-liner use ```--compact``` and ```--no-formatting```.
In order to generate more compact code the flag `--compact` can be passed to `convertfromhtml`.
This will generate the most compact python code and works with and without `--no-formatting`.
In order to get human readable code the flag `--compact` is recommended.
In order to get code fast (especially for big pages) the flag `--no-formatting` is recommended.
In order to get a one-liner use `--compact` and `--no-formatting`.

*Notes regarding the encoding of the input file:*
_Notes regarding the encoding of the input file:_

In case the encoding of the input HTML source file differs from the default
encoding of the operating system, there is the flag ```--encoding <encoding>```
encoding of the operating system, there is the flag `--encoding <encoding>`
which allows to enforce the given encoding when reading the file. This is
likely the case on Windows systems. It is therefore recommended to use
```--encoding utf8``` when using ```convertfromhtml``` on windows.

`--encoding utf8` when using `convertfromhtml` on windows.

Django integration
------------------
## Django integration

In order to use the element tree renderer in django html templates it is necessary to add a template tag which calls the render function.

Expand All @@ -301,11 +287,10 @@ def render_layout_to_response(request, layout, context):
return HttpResponse(layout.render(context))
```


Rational
--------
## Rational

Notes:

- Want internal/embeded DSL, access to document object model, not string interpolation
- https://wiki.python.org/moin/Templating ==> All dead links
- Existing HTML-DSL have no support for more abstract concepts like iterators and lazy values
Expand Down

0 comments on commit 88510d7

Please sign in to comment.