Skip to content

BMX - Basic Markup eXpressions: A Python DSL for HTML and XML

License

Notifications You must be signed in to change notification settings

stuartpullinger/bmx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

About

BMX - Basic Markup eXpressions: A Python DSL for HTML and XML

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages