PyconIE 2016 - Kajiki, the fast and validated template engine your were looking for
-
Upload
amol -
Category
Technology
-
view
96 -
download
2
Transcript of PyconIE 2016 - Kajiki, the fast and validated template engine your were looking for
KAJIKITHE FAST AND VALIDATED TEMPLATE
ENGINE YOU WERE LOOKING FOR
Alessandro Molina@__amol__
Who am I
● Passionate Python Developer
● TurboGears2 core team member
● Beaker caching/session framework current maintainer
● Author of DukPy js env for Python and DEPOT file storage framework
● Contributor to Ming, ToscaWidgets2, Formencode, WebOb, Kajiki, ...
Why?
● There are tens of template engines out there, but people often only know the default one of their web framework.
● Different template engines can differ in important features, not just syntax.
● Working on one has been fun and interesting.
Template Engines
TYPE NAME URL
Markup + Streamed Genshi http://genshi.edgewall.org/
Text + Compiled Mako http://www.makotemplates.org/
Text + Compiled Jinja http://jinja.pocoo.org/
Markup + Compiled Kajiki http://kajiki.readthedocs.io/
One more Template Engine?
● We loved the fact that Genshi templates could by opened with any HTML editor
● We loved Genshi syntax, concise and reflecting the output itself.
● We loved Genshi so much TG2.2 included it in all projects, to make it available for pluggable apps and extensions.
Right Timing...
How I felt...
Well...
● Genshi was pretty complex. We didn’t want to maintain such complexity.
● Streaming was powerful but hard to use, very few people understood genshi inheritance. And it was really slow.
● A TG team member was experimenting with the idea of a similar engine.
Kajiki<html>
<head>
<title py:content="title">This is replaced.</title>
</head>
<body>
<p>These are some fruits:</p>
<ul>
<li py:for="fruit in fruits">
I like ${fruit}s
</li>
</ul>
</body>
</html>
Directives● py:if & py:else● py:switch & py:case & py:else● py:for● py:def● py:strip● py:with● py:attrs● py:block & py:extends● ...
How It Works
● Your template is parsed and converted to Python code.
● Whenever the template is rendered the generated python code is executed.
● The python code is yielded by a generator that resembles the DOM of the document.
Architecture
PARSER
COMPILER
INTERMEDIATE REPRESENTATION
PYTHON
READ XML AND GENERATE DOM OUT OF IT WITH SAX
NAVIGATE DOM AND CREATE IR FOR ITS NODES
ITERATE THE IR NODES TO GENERATE PYTHON CODE
RUN PYTHON CODE TO RENDER TEMPLATE
Compiled Kajikiclass template:
@kajiki.expose
def __main__():
yield u'<html>\n <head>\n <title>'
yield self.__kj__.escape(title)
yield u'</title>\n </head>\n <body>\n <p>'
yield local.__kj__.gettext(u'These are some fruits:')
yield u'</p>\n <ul>\n '
for fruit in fruits:
yield u'<li>'
yield local.__kj__.gettext(u'\n I like ')
yield self.__kj__.escape(fruit)
yield local.__kj__.gettext(u's\n ')
yield u'</li>'
yield u'\n </ul>\n </body>\n</html>'
Rendered Kajiki<html>
<head>
<title>A Kajiki Template</title>
</head>
<body>
<p>These are some fruits:</p>
<ul>
<li>
I like oranges
</li><li>
I like apples
</li>
</ul>
</body>
</html>
That makes it pretty Fast
● Mako
○ Rendered 1M mako templates in 22.217651844
● Kajiki
○ Rendered 1M Kajiki templates in 11.8710489273
%for user in users:
<span>Hello {{ user }}!</span>
%endfor
<span py:for="user in users">
Hello ${user}!
</span>
It’s really just python! Mad science included!import kajiki
loader = kajiki.FileLoader('.', force_mode='xml')
tmpl = loader.load('mypage.kajiki')
with open('cython_test.pyx', 'wb') as pyx:
py_text = tmpl.py_text.replace('@kajiki.expose', '@staticmethod')
py_text = py_text.replace('__main__():', '__main__(self, local, users):')
py_text = py_text.replace('template = kajiki.Template(template)', '')
pyx.write(py_text)
import pyximport; pyximport.install()
import cython_test
class template:
@kajiki.expose
def __main__():
return cytmpl.__main__(self, local, users)
template = kajiki.Template(template)
print template(dict(cytmpl=cython_test.template, users=range(100000))).render()
Don’t try this at home
● Huge hack, but theoretically it can work. With minor tweaks Kajiki itself could generate cython compatible code.
● Rendering python 11.86215686798
● Rederning cython 9.07893800735
But being Python is easy to debug
Integrates with Python debuggers
Python Syntax Checking
kajiki.template.KajikiSyntaxError: [<string>:9]
invalid syntax
yield local.__kj__.gettext(u'These are some of
my favorite fruits:')
yield u'</p>\n <ul>\n '
--> for fruit on fuits:
yield u'<li>'
yield local.__kj__.gettext(u'\n I like ')
Validated Templates
● As Kajiki understands the document you are writing (it’s not just text) it can take steps specific to HTML generation:○ Error Reporting○ Escaping○ Automatic i18n○ Minification
Malformed HTML Detection
kajiki.xml_template.XMLTemplateParseError:
[./plain.kajiki:10] mismatched tag
<li py:for="fruit in fruits">
I like ${fruit}s
--> </span>
</ul>
</body>
Escaping handled for us<li py:for="fruit in fruits">
I like ${fruit}s
</li>
template(dict(
fruits=['<apple>'],
title='A Kajiki Template'
)).render()
<li>
I like <apple>s
</li>
Easy Translations<li py:for="fruit in fruits">
I like ${fruit}s
</li>
for fruit on fuits:
yield u'<li>'
yield local.__kj__.gettext(u'\n I like ')
Minificationtmpl = loader.load('plain.kajiki', strip_text=False)
<ul>
<li>
I like oranges
</li><li>
I like apple
</li><li>
I like kiwis
</li>
</ul>
tmpl = loader.load('plain.kajiki', strip_text=True)
<ul><li>I like oranges</li><li>I like apple</li><li>I like
kiwis</li></ul>
Text engine available too>>> Template = kajiki.TextTemplate('''
... {%if foo %}
... bar
... {%else%}
... baz
... {%end%}
... ''')
>>> print(Template(dict(foo=True)).render())
bar
>>> print(Template(dict(foo=False)).render())
baz
Feel free to try it!
● Python 2.6, 2.7, 3.2, 3.3, 3.4 and 3.5
● pip install kajiki
● Come and try it!
https://github.com/nandoflorestan/kajiki
● Still young! Feel free to open issues, send
pull requests, suggest features!
Questions?