A Python DSL to write HTML using Python objects
Project description
jvincipy — Python DSL for HTML
Extended project: full HTML5 tag coverage + CLI + Flask example
This document contains the full project layout, implementation, CLI and a minimal Flask app that demonstrates serving rendered pages.
Project structure
jvincipy_dsl/
├── README.md
├── pyproject.toml
├── jvincipy/
│ ├── __init__.py
│ ├── core.py
│ ├── tags.py
│ └── cli.py
├── examples/
│ ├── example.py
│ └── flask_app.py
└── tests/
└── test_basic.py
Note: The code below is intended to be copied into the corresponding files inside
jvincipy/,examples/and so on. The library is dependency-free except for the Flask example (which requires Flask).
Implementation (files)
jvincipy/core.py
(keeps the same core implementation as before: Tag, Markup, VOID_ELEMENTS, tag() helper and rendering logic.)
See the canvas file
jvincipy/core.pyfor the full source.
jvincipy/tags.py — now includes all HTML5 elements and common SVG elements
This file programmatically creates TagFactory objects for a comprehensive list of element names. The list includes:
See the canvas file
jvincipy/tags.pyfor the exact expanded list and generated factories.
jvincipy/css.py — Pythonic CSS DSL
This file provides a small, dependency-free API for building CSS rules and stylesheets in Python. It includes:
Rule: Represents a CSS rule with a selector and declarations. Supports nested rules and dict-based selectors.AtRule: For at-rules like@mediaand@keyframes, which wrap other rules.Stylesheet: Collects rules and at-rules, and renders the full CSS.
Usage Example:
from jvincipy.css import Rule, Stylesheet
# Create a rule
main_rule = Rule('body', {'margin': 0, 'font-family': 'sans-serif'})
main_rule.set(color='black')
# Add a nested rule
main_rule.add_nested('a', {'color': 'blue', 'text-decoration': 'none'})
# At-rule example
media = AtRule('@media (max-width:600px)')
media.add_rule(Rule('body', {'font-size': '14px'}))
# Build stylesheet
sheet = Stylesheet()
sheet.rules.append(main_rule)
sheet.at_rules.append(media)
print(sheet.rules[0].render(pretty=True))
print(sheet.at_rules[0].render())
Features:
- Python dicts for declarations, snake_case auto-converted to kebab-case.
- Nested selectors via dicts or explicit nesting.
- At-rules for media queries, keyframes, etc.
- Renders readable CSS with indentation.
See jvincipy/css.py for full API and helpers.
jvincipy/cli.py — a small CLI to render .py files to HTML
Behavior
-
Accepts a path to a Python file that creates a
pagevariable (aTaginstance) or defines a functionrender()which returns aTagor string. -
Loads and executes the file safely with
runpy.run_path()and then findspageorrenderin the executed module's globals. -
Writes an output HTML file or prints to stdout.
Usage
# render and pretty-print to stdout
python -m jvincipy.cli input.py --pretty
# render and write to output file
python -m jvincipy.cli input.py -o out.html
Exact code is available in
jvincipy/cli.pyin the canvas (includesargparse, error messages, and examples).
examples/flask_app.py — minimal Flask integration
This example shows how to use the library inside a Flask route. It expects you to have Flask installed.
Run:
pip install flask
python examples/flask_app.py
# then open http://127.0.0.1:5000/
See the file
examples/flask_app.pyin the canvas for the full code. It demonstrates two routes:/(renders apagecreated with jvincipy) and/raw(renders raw HTML string or Markup).
pyproject.toml — console script
The project includes an entry point so you can run the CLI as jvincipy after installing:
[project.scripts]
jvincipy = "jvincipy.cli:main"
(If you prefer not to install, run via python -m jvincipy.cli.)
How the CLI decides what to render
When you point the CLI at a Python file, it will run it and look for the following (in order):
-
a global variable named
pagethat is aTaginstance (or a string), or -
a callable
render()function that returns aTagor string, or -
a
get_page()function which returns aTagor string.
If none are found, the CLI exits with an error message describing what it expects.
Flask example behavior
The Flask example demonstrates:
-
importing the library (
from jvincipy import html, head, body, h1, p, Markup, ...) -
building a tag tree and returning a fully-formed HTML response using
page.render(pretty=True) -
using
Markupto inject pre-rendered HTML safely when needed
The example also shows how to pass query parameters to choose pretty/compact rendering.
Tests
The tests/test_basic.py remains the same — a minimal test ensuring a simple div(p('hello')) renders correctly.
Next steps I implemented in the canvas
-
Expanded
tags.pyto include a comprehensive HTML5 + SVG tag list and exported names. -
Added
jvincipy/cli.pythat provides a usable CLI withargparse, helpful errors, and--pretty/--minifyoptions. -
Added
examples/flask_app.pyshowing how to wire the DSL into Flask routes. -
Updated
pyproject.tomlsnippet with an installable console script entry.
How you can run and test locally (commands)
- Create a virtual environment and install dev/test dependencies (optional):
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install pytest
- Run the examples using the module CLI:
# render example.py to stdout
python -m jvincipy.cli examples/example.py --pretty
# render and save
python -m jvincipy.cli examples/example.py -o dist/example.html
- Try the Flask example:
pip install flask
python examples/flask_app.py
# open http://127.0.0.1:5000/
- Run tests:
pytest
Usage patterns & examples
1) Build pages programmatically
Create a Python file that constructs a page object using tag factories and run it with the CLI or import the module from an app.
2) Build CSS programmatically
Use the CSS DSL to generate stylesheets in Python:
from jvincipy.css import Stylesheet, Rule
sheet = Stylesheet()
sheet.rules.append(Rule('body', {'margin': 0, 'color': 'black'}))
print(sheet.rules[0].render())
You can combine this with HTML generation for full Python-powered web pages. 2) CLI rendering
The CLI expects the executed file to expose either:
- a global
page(Tag or string), or - a callable
render()/get_page()that returns a Tag/string.
3) Flask integration
Use the Flask example to serve static files from an assets/ directory and return page.render(pretty=True) in a route.
4) CSS integration
You can use the CSS API to generate stylesheets and serve them in Flask or write to files for static sites.
How to extend
- Extend the CSS DSL with more helpers, custom at-rules, or minification.
Contributing
Please read CONTRIBUTING.md for the suggested workflow. In short:
- Fork the repo
- Create a small branch with focused commits
- Add tests for new behavior
- Open a PR with a clear description and any migration notes
License
This project is released under the MIT License — see the LICENSE file.
CODE_OF_CONDUCT
A CODE_OF_CONDUCT.md has been added to help foster a welcoming community. The project follows the Contributor Covenant v2.1 with a zero-tolerance policy for harassment.
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 jvincipy-0.1.0.tar.gz.
File metadata
- Download URL: jvincipy-0.1.0.tar.gz
- Upload date:
- Size: 14.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dedab60f4f5f1668bf6adac8938d4c9ebbf95487f870a382e3ad6913604d2991
|
|
| MD5 |
bbe7733fa560c9e6ac7462cdcfe363a7
|
|
| BLAKE2b-256 |
c591b1ac5ac83b0ce9a7affc567b065fb08d64d9c04571dec2e5eb16779062e9
|
File details
Details for the file jvincipy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jvincipy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 10.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d4d75b12bb65900274a17468afaac34d965da9221353329eaa19004dfa9a124
|
|
| MD5 |
cfdea9e1a8a1ecaa5d79393dfd8517d4
|
|
| BLAKE2b-256 |
86d8e5c88e0690047fef423f80b21a647842df3a1c212fd61abcd9fb281ec293
|