Package: dashml
DashML - HTML Components in PythonCreate functions to build HTML in Python- inspired by the "component movement" in Javascript.>>>fromdashmlimport_,render>>>>>>render(_.p("Hello, world!"))'<p>Hello, world!</p>'Why DashML?JavaScript frameworks like React or Vue took over the frontend landscape because of the ease of creating reusable components. Reactespeciallythrives with the fact that components arecode, and can be manipulated as such.Meanwhile, server-side languages are stuck with clunky template languages to generate HTML. It's hard to extract components out of Jinja2 or Django templates to re-use.DashML expands on existing Python libraries to create an ergonomic way to generate HTML in Python.Built onlxml(and in turn built on C) for extreme speed- check out thebenchmarks(or run them yourself!).A minimal API you can pick up in ~15 minutes- DashML is so simple, you could have written it yourself!Adopting DashML in your project allows you to create traditional server rendered webpages faster, while embracing the good parts of component driven design.Get StartedTo get started with DashML, you will need:Python 3.6+pipAbout 5 minutes.Install From PyPiLike almost every Python package, you can install DashML from PyPi:pipinstalldashmlWow, that was really simple.Writing ComponentsDashML components are just functions that returnlxmlElementobjects. You can write one like this:# greeter.pyfromdashmlimport_defgreeter(name:str)->'Element':"""My first DashML component!!"""return_.p(f"Hello,{name}!")The functiongreeterreturns anElement, which is a special representation of an xml document from the librarylxml.lxmluses C, so it's really fast to create and modify elements.You can get a string out of anElementby using the DashML functionrender.# greeter.pyfromdashmlimportrender,_# ...snipprint(render(greeter("Maddie")))If you save this togreeter.pyand run it, you can see your component get rendered out as a string:pythongreeter.py
<p>Hello,Maddie!</p>Adding AttributesHTML documents almost always have attributes on some elements. You can add in attributes to DashML functions as keyword arguments.# greeter.pyfromdashmlimport_defgreeter(name:str)->'Element':"""My first DashML component!!"""return_.p(f"Hello,{name}!",id="my-greeter",class_name="my-greeter")DashML attributes are always snake case. The attributesclassandforare replaced withclass_nameandhtml_for. Additionally, attributes prefixed withdata_oraria_are converted to use dashes instead of underscores. If you've used React, this probably feels familiar!:warning: Unsafe Usage :warning:By default, DashML prevents XSS attacks byescapingall text that goes into any elements. If you really want to use a raw HTML string, you can importunsafe_from_stringand create raw elements that way.fromdashmlimportunsafe_from_stringelement=unsafe_from_string('<script>alert("Unsafe!")</script>')Remember,this is not safe for untrusted strings,and should only be used as an escape hatch if absolutely necessary. To stay safe, stick to normal usage of DashML.DashML TipsCongrats! You've completed a tour of the DashML API. Here are some friendly suggestions of how to use DashML effectively in your projects.Create composable, reusable functions.Writing out element names by hand everywhere isno betterthan just writing plain HTML, and doesn't use the composable and reusable powers DashML has.For example, rather than typing out_.h1(...)everywhere, create a function calledheaderthat is specific to your project and creates the elements you need:defheader(text:str)->'Element':"""Creates a header for a page."""return_.header(_.h1(text,class_name="header__title"),class_name="header")Composing functions that create elements is expressive, and a lot cleaner:page(header("HTML in Python? More likely than you think."),content(),footer(),)Now that's component driven code!Work With Your Data StructuresA great strategy with DashML components is to have them accept your data objects as arguments, and just control display logic.For example, if you're working with an ORM object for a blog post, creating a component calledblog_postprovides an easy way to render out your database records:# components.pydefblog_post(post:BlogPost)->'Element':returnpage(header(post.title),content(post.text),footer(),)Keep DashML Components SeparateConsider keeping your DashML code in a different module from your code that contains business logic (and everything else). Keeping a separatecomponents.pyfile in each module with DashML components keeps your intent clear and code clean:mymodule/
|- components.py
|- models.py
|- views.pyAdditionally, it allows easier mirroring of objects in other modules. From the blog post example above:# views.pyfrom.importcomponentsdefmy_view(request,post_id:int):# ...snippost=BlogPost.get(post_id)returncomponents.blog_post(post)...And Keep Business Logic out of ComponentsComponents should be as slim as possible, and only handle presentation logic. It's an anti-pattern to bundle up business logic in your DashML components.# BAD: Don't do this!defrender_post(pk:int):post=get_or_404(BlogPost,pk)return_.p(post.content)# GOOD: Just pass in objects to render!defrender_post(post:BlogPost):return_.p(post.content)Additionally, every DashML component is ideally apure function, that will always return the same result for a given input.Consider Functional CSSIf you want to bring functional styling into your DashML components, consider using a css library likeTachyonsfor styling.defmy_text(text:str):return_.p(text,class_name="f5 f4-l lh-copy athelas")Atomic/functional CSS blendsexceptionallywell with DashML, and allows the embracing of functional components in styles and markup.BenchmarksSince DashML relies onlxmlfor components, it's fast. Very, very fast. Rendering components is generally a sub-millisecond operation if no external action happens in the component.===============================================================
Benchmark: 'Simple HTML document.'
===============================================================
344.982 miliseconds for 5000 renders of 'Simple HTML document.'
Avg 0.069 miliseconds for one render.Obviously, adding things like HTTP requests, database queries, or complex calculations to renders will slow them down. But why are you doing that,keep logic separate!.You can run the benchmarks yourself locally by cloning and installing the project:gitclonehttps://github.com/madelyneriksen/dashml/cddashml
python-mvenv.env&&source.env/bin/activate&&pipinstall-rrequirements.dev.txt
pythonbench.py# or `make bench`Special ThanksDashML could not be built without these libraries:lxmlfor creating afastXML library.LicenseCopyright 2019 under terms of the MIT license. SeeLICENSEfor details.