katagami - a very simple xml template engine.
Project description
setup…skip to next chapter:
>>> def testfile(file): ... if not hasattr(file, 'read'): ... with open(file) as fp: ... result = render(fp) ... else: ... result = render(file) ... if sys.platform != 'cli': # IronPython doesn't implement unicode. ... result = result.encode() ... return result >>> def teststr(t): ... result = render('<html><body>%s</body></html>' % t, {'__file__': '<string>'}) ... if sys.platform != 'cli': # IronPython doesn't implement unicode. ... result = result.encode() ... return result >>> def echo(name, body): ... with open(os.path.join(tmpdir, name), 'w') as fp: ... fp.write(body) >>> def echoxml(name, body): ... echo(name, '<html><body>%s</body></html>' % body)
Pythonic evaluation
Dump the value (call eval with attribute value):
>>> teststr('''<python value="'hello, world'"/>''') '<html><body>hello, world</body></html>'
By the default, xml entities are escaped in value attribute of python element:
>>> teststr('''<python value="'<p>hello, world</p>'" escape="false"/>''') '<html><body><p>hello, world</p></body></html>'
By the default, exceptions are dumped in output (and see a helpful traceback):
>>> teststr('''<python value="not_found"/>''') '<html><body><pre>Traceback (most recent call last):\n File "<string>", line 1, in Element "python"\n File "<string>", line 1, in Element "python" at line 1-1\n not_found\nNameError: name \'not_found\' is not defined\n</pre></body></html>'
Attribute evaluation (attribute value starts with ‘python:’):
>>> teststr('''<p class="python:'python-expr'">hello, world</p>''') '<html><body><p class="python-expr">hello, world</p></body></html>'
Pythonic statements
All statements are available in all elements.
if, elif, else statements:
>>> teststr(''' ... <p if="0"/> ... <p elif="0"/> ... <p else="">output here</p> ... ''') '<html><body><p>output here</p></body></html>'
for statement (attribute value is Pythonic for style):
>>> teststr('''<p for_="i, j in enumerate(range(3))"><python value="i, j"/></p>''') '<html><body><p>(0, 0)</p><p>(1, 1)</p><p>(2, 2)</p></body></html>'
while statement:
>>> teststr(''' ... <python><![CDATA[ i = [1, 2, 3] ]]></python> ... <p while="i"> ... <python value="i[0]"/> ... <python><![CDATA[ i = i[1:] ]]></python> ... </p> ... ''') '<html><body><p>1</p><p>2</p><p>3</p></body></html>'
except statement:
>>> teststr(''' ... <python except="StandardError as e"><![CDATA[ not_found ]]></python> ... <python value="e"/> ... <python except="StandardError"><![CDATA[ not_found ]]></python> ... ''') "<html><body>name 'not_found' is not defined</body></html>"
with statement:
>>> echo('msg.txt', 'hello, world') >>> teststr(''' ... <python with="open(r'%s') as fp"> ... <p><python value="fp.read()"/></p> ... <p><python value="fp.closed"/></p> ... </python> ... <p><python value="fp.closed"/></p> ... ''' % os.path.join(tmpdir, 'msg.txt')) '<html><body><p>hello, world</p><p>False</p><p>True</p></body></html>'
Multi items are supported (ex. ‘with a, b: pass’).
>>> echo('msg2.txt', 'hello, world') >>> teststr(''' ... <python with="open(r'%s') as fp, open(r'%s') as fp2"> ... <p><python value="fp.read()"/></p> ... <p><python value="fp2.read()"/></p> ... </python> ... ''' % (os.path.join(tmpdir, 'msg.txt'), os.path.join(tmpdir, 'msg2.txt'))) '<html><body><p>hello, world</p><p>hello, world</p></body></html>'
def statement (give context by keyword arguments):
>>> teststr(''' ... <p def="myfunc">hello, <python value="msg"/></p> ... <python value="myfunc(msg='world')" escape="false"/> ... ''') '<html><body><p>hello,world</p></body></html>'
Embedded python script
CDATA is required and use write function like DOM’s document.write:
>>> teststr(''' ... <python><![CDATA[ ... write('<p>hello, world</p>') ... ]]></python> ... ''') '<html><body><p>hello, world</p></body></html>'
and escape xml entities:
>>> teststr(''' ... <python><![CDATA[ ... write('<p>', 'hello, world', '</p>', escape=True) ... ]]></python> ... ''') '<html><body><p>hello, world</p></body></html>'
Include python script file:
>>> echo('sub-script.py', '''write('hello, world')''') >>> echoxml('template.html', ''' ... <p><python src="sub-script.py"/></p> ... ''') >>> testfile(os.path.join(tmpdir, 'template.html')) '<html><body><p>hello, world</p></body></html>'
and share variables:
>>> echo('sub-script.py', ''' ... global msg ... msg = 'hello, world' ... msg2 = 'hello, world' ... global myfunc ... def myfunc(name): ... return 'hello, ' + name ... ''') >>> echoxml('template.html', ''' ... <python src="sub-script.py"/> ... <p><python value="msg"/></p> ... <p> ... <python value="msg2" except="NameError as e"/> ... <python value="e"/> ... </p> ... <p><python value="myfunc('world')"/></p> ... ''') >>> testfile(os.path.join(tmpdir, 'template.html')) "<html><body><p>hello, world</p><p>name 'msg2' is not defined</p><p>hello, world</p></body></html>"
Include another template
Simply, include all elements:
>>> echoxml('sub-template.html', '<p>hello, world</p>') >>> echoxml('template.html', '<python template="sub-template.html"/>') >>> testfile(os.path.join(tmpdir, 'template.html')) '<html><body><html><body><p>hello, world</p></body></html></body></html>'
Then include a part of elements:
>>> echoxml('sub-template.html', '<p id="myid">hello, world</p>') >>> echoxml('template.html', ... '<python template="sub-template.html" fragment="myid"/>') >>> testfile(os.path.join(tmpdir, 'template.html')) '<html><body><p id="myid">hello, world</p></body></html>'
And share variables:
>>> echoxml('sub-template.html', ''' ... <p id="myid">hello, world</p> ... <python><![CDATA[ ... global msg ... msg = 'hello, world' ... ]]></python> ... <p def="global myfunc"><python value="text"/></p> ... ''') >>> echoxml('template.html', ''' ... <python template="sub-template.html" fragment="myid"/> ... <p><python value="msg"/></p> ... <python value="myfunc(text='hello, world')" escape="false"/> ... ''') >>> testfile(os.path.join(tmpdir, 'template.html')) '<html><body><p id="myid">hello, world</p><p>hello, world</p><p>hello, world</p></body></html>'
Techniques and notices
This module is wrote under assuming that sys.setdefaultencoding(‘utf-8’).
- Use __mode__ for disabling any trick:
exception dump
treat exception as False in if and elif
special value named __noloop__
script indentation fix
>>> render('''<html __mode__="strict"><body><p if="fail"/></body></html>''') Traceback (most recent call last): ... NameError: name 'fail' is not defined
This module works with null xml namespace, but doesn’t remove any namespace:
>>> testfile(StringIO('''<?xml version="1.0"?> ... <root xmlns = "http://default-namespace.org/" ... xmlns:py = "http://www.python.org/ns/"> ... <py:elem1 py:if="0"/> ... <elem2 xmlns="" /> ... <py:elem3 if="0"/> ... </root>''')) '<?xml version="1.0"?>\n<root xmlns="http://default-namespace.org/" xmlns:py="http://www.python.org/ns/"><py:elem1 py:if="0"/><elem2 xmlns=""/></root>'
The namespace is flat like python module and nested in function:
>>> teststr(''' ... <python><![CDATA[ a = b = 0 ]]></python> ... <python def="myfunc"><![CDATA[ ... global a ... write('a = %d\\n' % a) ... write('b = %d\\n' % b) ... a = b = 1 ... ]]></python> ... <python value="myfunc()"/> ... <python value="a, b"/> ... ''') '<html><body>a = 0\nb = 0\n(1, 0)</body></html>'
By the default, White spaces and comments are stripped:
>>> teststr('''<p><!-- comment --> hello, world </p>''') '<html><body><p>hello, world</p></body></html>'
Use direct mode:
>>> teststr('''<p __mode__="direct"><!-- comment --> hello, world </p>''') '<html><body><p><!-- comment --> hello, world </p></body></html>'
The attribute order is important:
>>> teststr(''' ... <p if="0" for_="i in range(2)"><python value="i"/></p> ... <p for_="i in range(2)" if="i > 0"><python value="i"/></p> ... ''') '<html><body><p>1</p></body></html>'
If you need closing tag such as textarea, then write below:
>>> teststr('''<textarea><python/></textarea>''') '<html><body><textarea></textarea></body></html>'
Entities will not be expanded:
>>> teststr(''' &unknown_entity;''') '<html><body> &unknown_entity;</body></html>'
- Special variables are available in some cases:
__file__ = str -> path of file (template or script)
- __noloop__ = bool -> whether loop statements executed
(and when not strict mode)
_ = object -> temporary value when extracting variables
Special utility functions are available, see default_namespace.
- Special string codecs:
percent, uri - known as encodeURIComponent, decodeURIComponent
xml - escape ‘<’, ‘>’, ‘&’
For more information, see Element class implementation.
History
0.2.0 change exception handling, fix encoding handling
0.1.2 fix encoding handling, add new commandline handling
0.1.1 update document for PyPI
0.1.0 first release
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.