YDNATL is a Python library that lets you build HTML using simple Python classes.
Project description
YDNATL
YDNATL (You Don't Need Another Template Language) is a Python library that lets you build HTML using simple Python classes. It's great for apps that need HTML generation while skipping the hassle of writing it by hand or using a templating engine.
- ✓ Declarative syntax for building HTML documents (like Flutter)
- ✓ Easy to read and write
- ✓ Supports all HTML5 elements
- ✓ Lightweight
- ✓ Extremely fast
- ✓ Fully customisable
- ✓ Compose HTML efficiently
Requirements
Python 3.9 or higher is recommended.
Installation
pip install ydnatl
Usage
from ydnatl import *
# Create a simple HTML document
page = HTML(
Head(
Title("My Page")
),
Body(
Div(
H1("Hello, World!"),
Paragraph("This is a test document.")
)
)
)
# Render the HTML document
print(page.render())
This code will produce:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<title>My Page</title>
</head>
<body>
<div>
<h1>Hello, World!</h1>
<p>This is a test document.</p>
</div>
</body>
</html>
Dynamic Composition
from ydnatl import *
html = HTML()
header = Head()
body = Body()
body.append(
Div(
H1("My Headline"),
Paragraph("Basic paragraph element"),
)
)
if day_of_week == "Monday":
header.append(Title("Unfortunately, it's Monday!"))
else:
header.append(Title("Great! It's no longer Monday!"))
html.append(header)
html.append(body)
print(html.render())
All element classes are subclasses of HTMLElement. The parent class provides all of the inherited functionality to generated the individual tags. Keywords args used on methods will be converted to attributes on the HTML elements being generated.
Great For
- CLI tools
- Site builders
- Web frameworks
- Alternative to heavy template engines
- Static site generators
- Documentation generators
- LLM's and AI tooling that generate interfaces dynamically
- Creating frontends for headless platforms (CMS/CRM etc)
Examples
FastAPI
from fastapi import FastAPI
from ydnatl import *
app = FastAPI()
@app.get("/")
async def root():
return HTML(
Head(
Title("My Page")
),
Body(
Section(
H1("Hello, World!"),
Paragraph("This is a test document.")
)
)
)
Django
from django.http import HttpResponse
from ydnatl import *
def index(request):
return HttpResponse(HTML(
Head(
Title("My Page"),
Meta(charset="utf-8"),
Meta(name="viewport", content="width=device-width, initial-scale=1.0"),
Link(rel="stylesheet", href="style.css"),
Script(src="script.js")
),
Body(
Section(
H1("Hello, World!"),
Paragraph("This is a paragraph."),
Paragraph("This is another paragraph.")
)
)
))
Flask
from flask import Flask
from ydnatl import *
app = Flask(__name__)
@app.route("/")
def index():
return HTML(
Head(
Title("My Page")
),
Body(
Section(
H1("Hello, World!"),
Paragraph("This is a test document.")
)
)
)
Test Coverage
YDNATL has full test coverage. To run the tests locally, use:
pytest
or:
python run_test.py
Element Methods:
instance.prepend()instance.append()instance.filter()instance.remove_all()instance.clear()instance.pop()instance.first()instance.last()instance.add_attribute()instance.add_attributes()instance.remove_attribute()instance.get_attribute()instance.has_attribute()instance.generate_id()instance.clone()instance.find_by_attribute()instance.get_attributes()instance.count_children()instance.render()instance.to_dict()
Events
instance.on_load()instance.on_before_render()instance.on_after_render()instance.on_unload()
Element Properties
instance.taginstance.childreninstance.textinstance.attributesinstance.self_closing
Modules
| Module | Purpose | Examples |
|---|---|---|
| ydnatl.tags.form | Common HTML form elements | TODO |
| ydnatl.tags.html | Structural HTML document elements | TODO |
| ydnatl.tags.layout | Layout related HTML tags | TODO |
| ydnatl.tags.lists | HTML list elements | TODO |
| ydnatl.tags.media | Media related HTML elements | TODO |
| ydnatl.tags.table | HTML table elements | TODO |
| ydnatl.tags.text | HTML text elements | TODO |
Importing
Instead of importing the entire module, you can selectively use only the elements you need like this:
# Instead of
from ydnatl import *
# Import selectively
from ydnatl.tags.form import Form, Button
ydnatl.tags.form
Form()Input()Label()Select()Option()Button()Fieldset()Optgroup()
ydnatl.tags.html
HTML()Head()Body()Title()Meta()Link()Script()Style()IFrame()
ydnatl.tags.layout
Div()Section()Header()Nav()Footer()HorizontalRule()Main()
ydnatl.tags.lists
OrderedList()ListItem()Datalist()DescriptionDetails()DescriptionList()DescriptionTerm()
ydnatl.tags.media
Image()Video()Audio()Source()Picture()Figure()Figcaption()Canvas()
ydnatl.tags.table
Table()TableFooter()TableHeaderCell()TableHeader()TableBody()TableDataCell()TableRow()
ydnatl.tags.text
H1()H2()H3()H4()H5()H6()Paragraph()Blockquote()Pre()Quote()Cite()Em()Italic()Span()Strong()Abbr()Link()Small()Superscript()Subscript()Time()Code()
Creating your own elements or components
from ydnatl import *
class MyTag(HTMLElement):
def __init__(self, *args, **kwargs):
super().__init__(*args, **{**kwargs, "tag": "mytag"})
self.add_attributes([
("id", "mycustomid"),
("aria-controls", "menu"),
])
def on_load(self) -> None:
print("The on_load event has been called")
def on_before_render(self) -> None:
print("The on_before_render event has been called")
def on_after_render(self) -> None:
print("The on_after_render event has been called")
mytag = MyTag(
Div(
Paragraph("Hello World!")
)
)
print(mytag.render())
This will produce:
<mytag id="mycustomid" aria-controls="menu">
<div>
<p>Hello World!</p>
</div>
</mytag>
You can use the event callbacks or properties/methods directly to load further child elements, fetch data or any other programmatic task to enrich or contruct your tag on loading, render or even after render.
You can take this further and contruct an entire page as a component where everything needed for the page is contained within the element class itself. This is a great way to build websites.
Contributions
Contributions, suggestions and improvements are all welcome.
Developing YDANTL
- Create a virtual environment
python3.12 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
- Install the dev dependencies:
pip install ".[dev]"
- Run the tests:
python run_tests.py
When you are happy with your changes, create a merge request.
License
Please see LICENSE for licensing details.
Author
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ydnatl-1.0.4.tar.gz.
File metadata
- Download URL: ydnatl-1.0.4.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b7893f2a489a5a1c7c676c988cfec5227339c5e77cbb29da9d7e86d4886202e3
|
|
| MD5 |
2bb9517b60de559c33bb3dfdb7ea86e8
|
|
| BLAKE2b-256 |
b14d80d0747e7aa623b0d3590bda37da32cebe5bf2993f90ae33c9dba562b317
|
File details
Details for the file ydnatl-1.0.4-py3-none-any.whl.
File metadata
- Download URL: ydnatl-1.0.4-py3-none-any.whl
- Upload date:
- Size: 12.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1054d0d1c7d4202d154f45cd3918e11e925acd5c85de4185ddbe67595f9f391d
|
|
| MD5 |
5498155836a4a875512966413b879dd6
|
|
| BLAKE2b-256 |
57bd2c630c57a66cdf56926666fea24ca1889da9133a8c06134e0e0c78f0099f
|