Skip to main content

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)

Source distribution for bmx 0.0.3
File Size Uploaded
bmx-0.0.3.tar.gz 16.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bmx 0.0.3
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.0.3 This release

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page