BMX - Basic Markup eXpressions
A DSL for representing HTML/XML in Python using an expression-like syntax. Why? You get to use the Python syntax you already know.
Example
This example from the Jinja2 website can be represented in BMX like so:
mydoc = (
DOCTYPE.html
+html(lang='en')
+head
+title +"My Webpage" -title
-head
+body
+ul('#navigation')
+(
+li
+a(href=item.href) +item.caption -a
-li
for item in navigation)
-ul
+h1 +"My Webpage" -h1
+a_variable
# a comment
-body
-html)
Note: Just as with ordinary Python expressions, multi-line BMX expressions must be surrounded by parentheses.
Installation and Dependencies
bmx is tested on CPython versions 3.6-3.9. It has 2 dependencies: singledispatchmethod (backported from 3.8) and MarkupSafe - to escape html in strings.
pip install bmx
Usage
An example using Flask (available in the top-level source directory):
# flask_greeter.py
from bmx.htmltags import (
html,
head,
title,
body,
p
)
from flask import Flask
app = Flask(__name__)
@app.route('/<name>')
def greeter(name: str):
return str(
# fmt: off
+html
+head
+title +"Flask Greeter" -title
-head
+body
+p +f"Hello {name}" -p
-body
-html
# fmt: on
)
Install Flask then run it as:
FLASK_APP=flask_greeter.py flask run
Go to https://127.0.0.1:5000/<your_name> in your browser (eg. https://127.0.0.1:5000/Stuart) and you will see the message.
Table of Conversions
| Type | HTML | BMX | Comment/Mnemonic |
|---|---|---|---|
| Opening tag | <div> |
+div |
Mnemonic: Adding content |
| Closing tag | </div> |
-div |
Mnemonic: opposite of adding content |
| Self-closing tag | <input/> |
+input |
Self-closing tag are pre-defined |
| Attributes | <a href="/">Home</a> |
+a(href="/") +"Home" -a |
Mnemonic: attributes are keyword arguments. |
| Attributes | <button aria-label="Close">X</button> |
+button(aria_label="Close") +"X" -button |
Note: Underscores in keyword arguments are replaced with dashes |
| Attributes | <input type="text"> |
+input_(type_="text") |
Note: Append an underscore to avoid conflicts with Python keywords |
Attributes: shorthand for id and class |
<div id="userinput" class="credentials" > |
+div('#userinput.credentials') |
#id .classname |
Attributes: shorthand for class |
<div class="col-sm-8 col-md-7 py-4"> |
+div .col_sm_8 .col_md_7 .py_4 |
.classname Underscores are replaced with dashes |
| Composing tags and content | <h1>The Title</h1> |
+h1 +"The Title" -h1 |
Mnemonic: think string concatenation ie. "Hello " + "World!" |
How does it work?
We define a Tag class which overrides the unary +/- and binary +/- operators to model the opening and closing tags of HTML. We provide a __call__ method to model HTML attributes as keyword arguments and a __getattr__ method to provide a shorthand for HTML classes (see above). A Tag is instantiated for every HTML tag and is available with a from bmx.htmltags import html, head, body, span.
MarkupSafe
bmx uses MarkupSafe to escape HTML from strings. If you are sure that you don't want to escape the HTML in a string, you can wrap it in a Markup object and the string will be included as-is.
Autoformatters
Black
To use the Black uncompromising autoformatter, surround your BMX markup with #fmt: off and #fmt: on comments like this:
result = (
# fmt: off
+html
+body
+h1 +"My Page" -h1
-body
-html
# fmt: on
)
Autopep8
To use autopep8, you can use the #fmt: off and #fmt: on comments as above or turn off 2 fixes:
- E225 - Fix missing whitespace around operator.
- E131 - Fix hanging indent for unaligned continuation line.
whereever you put your autopep8 configuration
ignore = E225,E131
Changelog
0.0.3
Fixes for:
- Class list can only be created once #4
- Keyword arguments in snake_case should be translated to kebab-case #3
- README improvements/fixes
0.0.2
- default to using MarkupSafe for strings
- include DOCTYPE in htmltags module
- README improvements/fixes
0.0.1
- Initial release
Release files for bmx 0.0.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| bmx-0.0.3.tar.gz | 16.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| bmx-0.0.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.0 kB
Release files / bmx-0.0.3.tar.gz
| Download URL | bmx-0.0.3.tar.gz |
|---|---|
| Size | 16.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
179a1a93d5a8da2ec022d0db820a382b950c356173dbb741e422487a1b1203fd
|
|
BLAKE2b-256 checksum How to use checksums |
8eaacc9cbdd2a2f59fdc046299cef3416cc33b53335ba7fcadac501978dabff8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.1.2 CPython/3.6.9 Linux/4.19.128-microsoft-standard
|
Release files / bmx-0.0.3-py3-none-any.whl
| Download URL | bmx-0.0.3-py3-none-any.whl |
|---|---|
| Size | 14.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
699e7c2b47b3a4f5c2be6f735663c84f37e956c94c8181d3f87193408deb2e32
|
|
BLAKE2b-256 checksum How to use checksums |
1653e7ce2adf7dc265329576093673f9a9c49e124ce5a2d36bb7d79b0e24c192
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.1.2 CPython/3.6.9 Linux/4.19.128-microsoft-standard
|