Need help with TemPy?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

Hrabal
131 Stars 67 Forks Apache License 2.0 673 Commits 3 Opened issues

Description

Python Object Oriented Html Templating System

Services available

!
?

Need anything else?

Contributors list

TemPy

TemPy Logo

Release PyPI version PyPI version Build Status Coverage Status codebeat badge Code Climate Codacy Badge GitHub license Say Thanks! Downloads

Fast 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:

Usage

Installation

TemPy is available on PyPi:

pip3 install tem-py
.

Or clone/download this repository, and run

python3 setup.py install

Building the DOM

Basic Usage

TemPy offers clean syntax for building pages in pure python: ```python from tempy.tags import Html, Head, Body, Meta, Link, Div, P, A mytextlist = ['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 objects

page = Html()( # add tags inside the one you created calling the parent Head()( # add multiple tags in one call Meta(charset='utf-8'), # add tag attributes using kwargs in tag initialization Link(href="my.css", typ="text/css", rel="stylesheet") ), body=Body()( # give them a name so you can navigate the DOM with those names Div(klass='linkBox')( A(href='www.foo.com') ), (P()(text) for text in mytextlist), # tag insertion accepts generators another_list # add text from a list, str.join is used in rendering ) )

add tags and content later

page[1]0 # calling the tag page[1][0].append(A(href='www.baz.com')) # using the API link = A().append_to(page.body[0]) # access the body as if it's a page attribute page.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 tags

page.render()

    <div class="linkBox">
        <a href="https://github.com/Hrabal/TemPy/blob/master/www.foo.com">www.foo.com</a>
        <a href="https://github.com/Hrabal/TemPy/blob/master/www.bar.com">www.bar.com</a>
        <a href="https://github.com/Hrabal/TemPy/blob/master/www.baz.com">www.baz.com</a>
        <a href="https://github.com/Hrabal/TemPy/blob/master/www.python.org">This is a link to Python.</a>
    </div>
    <p>This is Foo.</p>
    <p>This is Bar.</p>
    <p>Have you met my friend Baz?</p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit
    <div></div>
</blockquote>
</blockquote>
</blockquote>

<h4>Building Blocks</h4>

<p>You can also create blocks, and put them together using the manipulation API. Each TemPy object can be used later inside another TemPy object:
```python</p>

<h1>--- file: base_elements.py</h1>

<p>from somewhere import links, foot_imgs</p>

<h1>define some common blocks</h1>

<p>header = Div(klass='header')(title=Div()('My website'), logo=Img(src='img.png'))
menu = Div(klass='menu')(Li()(A(href=link)) for link in links)
footer = Div(klass='coolFooterClass')(Img(src=img) for img in foot_imgs)
</p><pre>
</pre>python

<h1>--- file: pages.py</h1>

<p>from base_elements import header, menu, footer</p>

<h1>import the common blocks and use them inside your page</h1>

<p>home<em>page = Html()(Head(), body=Body()(header, menu, content='Hello world.', footer=footer))
content</em>page = Html()(Head(), body=Body()(header, menu, container=Div(klass='container'), footer=footer))
</p><pre>
</pre>python

<h1>--- file: my_controller.py</h1>

<p>from tempy.tags import Div
from pages import home<em>page, content</em>page</p>

<p>@controller<em>framework</em>decorator
def my<em>home</em>controller(url='/'):
    return home_page.render()</p>

<p>@controller<em>framework</em>decorator
def my<em>content</em>controller(url='/content'):
    content = Div()('This is my content!')
    return content_page.body.container.append(content).render()
```</p>

<h4>OOT - Object-Oriented Templating</h4>

<p>TemPy is designed to provide Object-Oriented Templating. You can subclass TemPy classes, and add custom HTML tree structures to use as blocks.</p>
<pre class="language-python">from tempy.widgets import TempyPage

class BasePage(TempyPage):
    def js(self):
        return [
            Script(src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"),
        ]

    def css(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 structure
    def init(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 class
    def make_menu(self, typ):
        return Div(klass='menu')(
                            Nav()(
                                Ul()(
                                    Li()(
                                        A(href=item[1])(item[0]))
                                    for item in self.get_menu(typ)
                                )
                            ),
                        )

    def get_menu(self, typ):
        return [(mi.name, mi.link)
                for mi in Menu.query.filter_by(active=True, menu=typ
                                               ).order_by(Menu.order).all()]

</pre>
<p>...you can then sublass your custom TemPy object to add a specific behavior:
```python
class HomePage(BasePage):</p>
<pre>def init(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()
        )
    )

def get_dynamic_content(self):
    # Here using SQLAlchemy:
    current_content = Content.query.outerjoin(Content.comments).order_by(Content.date.desc(), Content.id.desc()).limit(1).first()
    if not current_content:
        return 'No content today!'
    return Div()(Span()(current_content.title),
                 Span()(current_content.text)),
                 Div()(comment for comment in current_content.comments))
</pre><pre>
TemPy executes each base class `init` method in reverse MRO, so your subclass can access all the elements defined in its parent classes.

#### TemPy Reprs

Another way to use TemPy is to define a nested `TempyREPR` class inside your classes:
```python
class MyClass:
    def __init__(self):
        self.foo = 'foo'
        self.bar = 'bar'

    class HtmlREPR(TempyREPR):
        def repr(self):
            self(
                Div()(self.foo),
                Div()(self.bar)
            )
</pre>
<p>You can think the </p><pre>TempyREPR</pre> as a <pre>__repr__</pre> equivalent, so when an instance is placed inside a TemPy tree, the <pre>TempyREPR</pre> subclass is used to render the instance.

<p>You can define several </p><pre>TempyREPR</pre> nested classes, and when dealing with a non-TemPy object, TemPy will search for a <pre>TempyREPR</pre> subclass following this priority:
* a <pre>TempyREPR</pre> subclass with the same name of its TemPy container.
* a <pre>TempyREPR</pre> subclass with the same name of its TemPy container's root.
* a <pre>TempyREPR</pre> subclass named <pre>HtmlREPR</pre>.
* the first <pre>TempyREPR</pre> found.
* if none of the previous ones are found, the object will be rendered calling its <pre>__str__</pre> method.

<p>You can use this order to set different renderings for different situations/pages:</p>
<pre class="language-python">class MyClass:
    def __init__(self):
        self.foo = 'foo'
        self.bar = 'bar'
        self.link = 'www.foobar.com'

    # If an instance on MyClass is found inside a div
    class Div(TempyREPR):
        def repr(self):
            self(
                Div()(self.foo),
                Div()(self.bar)
            )

    # If an instance on MyClass is found inside a link
    class A(TempyREPR):
        def repr(self):
            self.parent.attrs['href'] = self.link
            self('Link to ', self.bar)

    # If an instance on MyClass is found inside a table cell
    class Td(TempyREPR):
        def repr(self):
            self(self.bar.upper())

    # If an instance on MyClass is found when rendering the a TempyPage called 'HomePage'
    class HomePage(TempyREPR):
        def repr(self):  # note: here self is the object's parent, not the root
            self('Hello World, this is bar: ', self.bar)
</pre>
<h3>Elements API</h3>

<p>Create DOM elements by instantiating tags:
```python
page = Html()</p>

<blockquote>
<blockquote>
<blockquote>
<p>
```</p>
</blockquote>
</blockquote>
</blockquote>

<p>Add elements or content by calling them like a function...
```python
page(Head())</p>

<blockquote>
<blockquote>
<blockquote>
<p></p>
<pre>
...or use one of the jQuery-like APIs:
</pre>python
body = Body()
page.append(body)

</blockquote>
</blockquote>
</blockquote>

<p>div = Div().append_to(body)</p>

<blockquote>
<blockquote>
<blockquote>
<p></p>
<div></div>
div.append('This is some content', Br(), 'And some Other')
<div>This is some content<br>And some Other</div>
<pre>
...same for removing:
</pre>python
head.remove()
<div></div>
body.empty()

page.pop()

Several APIs are provided to modify your existing DOM elements:

python
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 Attributes

Add attributes to every element at definition time or later: ```python div = Div(id='myhtmlid', klass='someHtmlClass') # 'klass' because 'class' is a Python's buildin keyword

a = A(klass='someHtmlClass')('text of this link') a.attr(id='anotherdomid') a.attr({'href': 'www.thisisalink.com'})

text of this link ```

Styles are editable in the jQuery fashion: ```python div2.css(width='100px', float='left') div2.css({'height': '100em'}) div2.css({'background-color': 'blue'})


DOM Navigation

All of the TemPy tag contents are iterable and accessible which is similar to a Python list. For example: ```python divs = [Div(id=div, klass='inner') for div in range(10)] ps = (P() for _ in range(10)) container_div = Div()(divs)

for i, div in enumerate(containerdiv): div.attr(id='divId'+str(i)) containerdiv[0].append(ps) container_div[0][4].attr(id='pId')


...or access elements inside a container as if they were attributes: ```python containerdiv = Div() containerdiv(content_div=Div())

containerdiv.contentdiv('Some content')

Some content

...or if you feel jQuery-ish you can use:

python
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 / Hrabal

Contribute.

All contributions are welcome. Please refer to the contributing page.

Python versions compatibility

Python >= 3.3 needed, ask Travis Build Status

Apache 2.0 license, see LICENSE for details.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.