Package: bmx
BMX - Basic Markup eXpressionsA DSL for representing HTML/XML in Python using an expression-like syntax. Why? You get to use the Python syntax you already know.ExampleThisexample 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-liforiteminnavigation)-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 Dependenciesbmxis tested on CPython versions 3.6-3.9. It has 2 dependencies: singledispatchmethod (backported from 3.8) and MarkupSafe - to escape html in strings.pipinstallbmxUsageAn example using Flask (available in the top-level source directory):# flask_greeter.pyfrombmx.htmltagsimport(html,head,title,body,p)fromflaskimportFlaskapp=Flask(__name__)@app.route('/<name>')defgreeter(name:str):returnstr(# 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.pyflaskrunGo tohttps://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 ConversionsTypeHTMLBMXComment/MnemonicOpening tag<div>+divMnemonic: Adding contentClosing tag</div>-divMnemonic: opposite of adding contentSelf-closing tag<input/>+inputSelf-closing tag are pre-definedAttributes<a href="/">Home</a>+a(href="/") +"Home" -aMnemonic: attributes are keyword arguments.Attributes<button aria-label="Close">X</button>+button(aria_label="Close") +"X" -buttonNote: Underscores in keyword arguments are replaced with dashesAttributes<input type="text">+input_(type_="text")Note: Append an underscore to avoid conflicts with Python keywordsAttributes: shorthand foridandclass<div id="userinput" class="credentials" >+div('#userinput.credentials')#id.classnameAttributes: shorthand forclass<div class="col-sm-8 col-md-7 py-4">+div .col_sm_8 .col_md_7 .py_4.classnameUnderscores are replaced with dashesComposing tags and content<h1>The Title</h1>+h1 +"The Title" -h1Mnemonic: think string concatenation ie. "Hello " + "World!"How does it work?We define aTagclass 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). ATagis instantiated for every HTML tag and is available with afrom bmx.htmltags import html, head, body, span.MarkupSafebmxuses 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.AutoformattersBlackTo use the Black uncompromising autoformatter, surround your BMX markup with#fmt: offand#fmt: oncomments like this:result=(# fmt: off+html+body+h1+"My Page"-h1-body-html# fmt: on)Autopep8To use autopep8, you can use the#fmt: offand#fmt: oncomments 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 autopep8configurationignore=E225,E131Changelog0.0.3Fixes for:Class list can only be created once #4Keyword arguments in snake_case should be translated to kebab-case #3README improvements/fixes0.0.2default to using MarkupSafe for stringsinclude DOCTYPE in htmltags moduleREADME improvements/fixes0.0.1Initial release