Package: tem-py
TemPyFast Object-Oriented HTML templating With Python!What?Build HTML without writing a single tag.
TemPy dynamically generates HTML and accesses it in a pure Python, or jQuery fashion. Navigating the DOM, and manipulating tags is also possible in a Python and/or jQuery-similar syntax.Why?HTML is like SQL. We all use it, we all know how it works, and we all recognize its importance. Our biggest dream is to never write a single line of it again. For SQL we have ORMs, but we are not there yet for HTML.
Templating systems are awesome (Python syntax in HTML code), but they are not awesome enough because you still have to write HTML. Thus the idea of TemPy emerged!Weeeeeeee!No parsing and a simple structure makes TemPy incredibly fast. TemPy simply adds HTML tags around your data, and the actual HTML string exists only at render time.Read the full documentation here:https://hrabal.github.io/TemPy/Build, manipulate, and navigate HTML documents, with no HTML involved.Overview:Building the DOM, basic usageBuilding separate blocks, nesting and re-usabilityOOT - Object-Oriented TemplatingTemPy REPR, set and forgetDOM elements API, change your page dynamicallyDOM navigation, it's a tree!Tag attributes, it's a tag!UsageInstallationTemPy is available on PyPi:pip3 install tem-py.Or clone/download this repository, and runpython3 setup.py installBuilding the DOMBasic UsageTemPy offers clean syntax for building pages in pure python:fromtempy.tagsimportHtml,Head,Body,Meta,Link,Div,P,Amy_text_list=['This is Foo.','This is Bar.','Have you met my friend Baz?']another_list=['Lorem ipsum ','dolor sit amet, ','consectetur adipiscing elit']# make tags instantiating TemPy objectspage=Html()(# add tags inside the one you created calling the parentHead()(# add multiple tags in one callMeta(charset='utf-8'),# add tag attributes using kwargs in tag initializationLink(href="my.css",typ="text/css",rel="stylesheet")),body=Body()(# give them a name so you can navigate the DOM with those namesDiv(klass='linkBox')(A(href='www.foo.com')),(P()(text)fortextinmy_text_list),# tag insertion accepts generatorsanother_list# add text from a list, str.join is used in rendering))# add tags and content laterpage[1][0](A(href='www.bar.com'))# calling the tagpage[1][0].append(A(href='www.baz.com'))# using the APIlink=A().append_to(page.body[0])# access the body as if it's a page attributepage.body(testDiv=Div())# WARNING! Correct ordering with named Tag insertion is ensured with Python >= 3.5 (because kwargs are ordered)link.attr(href='www.python.org')('This is a link to Python.')# Add attributes and content to already placed tagspage.render()>>><html>>>><head>>>><metacharset="utf-8"/>>>><linkhref="my.css"type="text/css"rel="stylesheet"/>>>></head>>>><body>>>><divclass="linkBox">>>><ahref="www.foo.com">www.foo.com</a>>>><ahref="www.bar.com">www.bar.com</a>>>><ahref="www.baz.com">www.baz.com</a>>>><ahref="www.python.org">ThisisalinktoPython.</a>>>></div>>>><p>ThisisFoo.</p>>>><p>ThisisBar.</p>>>><p>HaveyoumetmyfriendBaz?</p>>>>Loremipsumdolorsitamet,consecteturadipiscingelit>>><div></div>>>></body>>>></html>Building BlocksYou can also create blocks, and put them together using the manipulation API. Each TemPy object can be used later inside another TemPy object:# --- file: base_elements.pyfromsomewhereimportlinks,foot_imgs# define some common blocksheader=Div(klass='header')(title=Div()('My website'),logo=Img(src='img.png'))menu=Div(klass='menu')(Li()(A(href=link))forlinkinlinks)footer=Div(klass='coolFooterClass')(Img(src=img)forimginfoot_imgs)# --- file: pages.pyfrombase_elementsimportheader,menu,footer# import the common blocks and use them inside your pagehome_page=Html()(Head(),body=Body()(header,menu,content='Hello world.',footer=footer))content_page=Html()(Head(),body=Body()(header,menu,container=Div(klass='container'),footer=footer))# --- file: my_controller.pyfromtempy.tagsimportDivfrompagesimporthome_page,content_page@controller_framework_decoratordefmy_home_controller(url='/'):returnhome_page.render()@controller_framework_decoratordefmy_content_controller(url='/content'):content=Div()('This is my content!')returncontent_page.body.container.append(content).render()OOT - Object-Oriented TemplatingTemPy is designed to provide Object-Oriented Templating. You can subclass TemPy classes, and add custom HTML tree structures to use as blocks.fromtempy.widgetsimportTempyPageclassBasePage(TempyPage):defjs(self):return[Script(src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"),]defcss(self):return[Link(href=url_for('static',filename='style.css'),rel="stylesheet",typ="text/css"),Link(href='https://fonts.googleapis.com/css?family=Quicksand:300',rel="stylesheet"),Link(href=url_for('static',filename='/resources/font-awesome-4.7.0/css/font-awesome.min.css'),rel="stylesheet"),]# Define the init method as a constructor of your block structuredefinit(self):self.head(self.css(),self.js())self.body(container=Div(id='container')(title=Div(id='title')(Div(id='page_title')(A(href='/')('MySite')),menu=self.make_menu('MAIN')),content=Div(id='content')(Hr())))# Your subclass can have its own methods like any other classdefmake_menu(self,typ):returnDiv(klass='menu')(Nav()(Ul()(Li()(A(href=item[1])(item[0]))foriteminself.get_menu(typ))),)defget_menu(self,typ):return[(mi.name,mi.link)formiinMenu.query.filter_by(active=True,menu=typ).order_by(Menu.order).all()]...you can then sublass your custom TemPy object to add a specific behavior:classHomePage(BasePage):definit(self):self.body.container.content(Div()(Br(),'This is my home page content',Br(),H3()('Hame page important content'),'Look, I\'m a string!',Br(),H3()('H3 is big, really big'),H1()('Today\'s content:'),self.get_dynamic_content()))defget_dynamic_content(self):# Here using SQLAlchemy:current_content=Content.query.outerjoin(Content.comments).order_by(Content.date.desc(),Content.id.desc()).limit(1).first()ifnotcurrent_content:return'No content today!'returnDiv()(Span()(current_content.title),Span()(current_content.text)),Div()(commentforcommentincurrent_content.comments))TemPy executes each base classinitmethod in reverse MRO, so your subclass can access all the elements defined in its parent classes.TemPy ReprsAnother way to use TemPy is to define a nestedTempyREPRclass inside your classes:classMyClass:def__init__(self):self.foo='foo'self.bar='bar'classHtmlREPR(TempyREPR):defrepr(self):self(Div()(self.foo),Div()(self.bar))You can think theTempyREPRas a__repr__equivalent, so when an instance is placed inside a TemPy tree, theTempyREPRsubclass is used to render the instance.You can define severalTempyREPRnested classes, and when dealing with a non-TemPy object, TemPy will search for aTempyREPRsubclass following this priority:aTempyREPRsubclass with the same name of its TemPy container.aTempyREPRsubclass with the same name of its TemPy container's root.aTempyREPRsubclass namedHtmlREPR.the firstTempyREPRfound.if none of the previous ones are found, the object will be rendered calling its__str__method.You can use this order to set different renderings for different situations/pages:classMyClass:def__init__(self):self.foo='foo'self.bar='bar'self.link='www.foobar.com'# If an instance on MyClass is found inside a divclassDiv(TempyREPR):defrepr(self):self(Div()(self.foo),Div()(self.bar))# If an instance on MyClass is found inside a linkclassA(TempyREPR):defrepr(self):self.parent.attrs['href']=self.linkself('Link to ',self.bar)# If an instance on MyClass is found inside a table cellclassTd(TempyREPR):defrepr(self):self(self.bar.upper())# If an instance on MyClass is found when rendering the a TempyPage called 'HomePage'classHomePage(TempyREPR):defrepr(self):# note: here self is the object's parent, not the rootself('Hello World, this is bar: ',self.bar)Elements APICreate DOM elements by instantiating tags:page=Html()>>><html></html>Add elements or content by calling them like a function...page(Head())>>><html><head></head></html>...or use one of the jQuery-like APIs:body=Body()page.append(body)>>><html><head></head><body></body></html>div=Div().append_to(body)>>><html><head></head><body><div></div></body></html>div.append('This is some content',Br(),'And some Other')>>><html><head></head><body><div>Thisissomecontent<br>AndsomeOther</div></body></html>...same for removing:head.remove()>>><html><body><div></div></body></html>body.empty()>>><html><body></body></html>page.pop()>>><html></html>Several APIs are provided to modify your existing DOM elements:div1=Div()div2=Div()div1.after(div2)div1.before(div2)div1.prepend(div2)div1.prepend_to(div2)div1.append(div2)div1.append_to(div2)div1.wrap(div2)div1.wrap_inner(div2)div1.replace_with(div2)div1.remove(div2)div1.move_childs(div2)div1.move(div2)div1.pop(div2)div1.empty(div2)div1.children(div2)div1.contents(div2)div1.first(div2)div1.last(div2)div1.next(div2)div1.next_all(div2)div1.prev(div2)div1.prev_all(div2)div1.siblings(div2)div1.slice(div2)Tag AttributesAdd attributes to every element at definition time or later:div=Div(id='my_html_id',klass='someHtmlClass')# 'klass' because 'class' is a Python's buildin keyword>>><divid="my_dom_id"class="someHtmlClass"></div>a=A(klass='someHtmlClass')('text of this link')a.attr(id='another_dom_id')a.attr({'href':'www.thisisalink.com'})>>><aid="another_dom_id"class="someHtmlClass"href="www.thisisalink.com">textofthislink</a>Styles are editable in the jQuery fashion:div2.css(width='100px',float='left')div2.css({'height':'100em'})div2.css({'background-color':'blue'})>>><divid="another_dom_id"class="someHtmlClass comeOtherClass"style="width: 100px; float: left; height: 100em; background-color: blue"></div>DOM NavigationAll of the TemPy tag contents are iterable and accessible which is similar to a
Python list. For example:divs=[Div(id=div,klass='inner')fordivinrange(10)]ps=(P()for_inrange(10))container_div=Div()(divs)fori,divinenumerate(container_div):div.attr(id='divId'+str(i))container_div[0].append(ps)container_div[0][4].attr(id='pId')>>><div>>>><divid="divId0">>>><p></p>>>><p></p>>>><p></p>>>><p></p>>>><pid="pId"></p>>>><p></p>>>><p></p>>>><p></p>>>><p></p>>>><p></p>>>></div>>>><divid="divId1"></div>>>><divid="divId2"></div>>>><divid="divId3"></div>>>><divid="divId4"></div>>>><divid="divId5"></div>>>><divid="divId6"></div>>>><divid="divId7"></div>>>><divid="divId8"></div>>>><divid="divId9"></div>>>></div>...or access elements inside a container as if they were attributes:container_div=Div()container_div(content_div=Div())container_div.content_div('Some content')>>><div><div>Somecontent</div></div>...or if you feel jQuery-ish you can use:container_div.children()container_div.first()container_div.last()container_div.next()container_div.prev()container_div.prev_all()container_div.parent()container_div.slice()Credits: made and maintained by Federico Cerchiari / HrabalContribute.All contributions are welcome. Please refer to thecontributing page.Python versions compatibilityPython >= 3.3 needed, askTravisApache 2.0 license, see LICENSE for details.