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): ... return testfile(StringIO('<html><body>%s</body></html>' % t)) >>> 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 suppressed:
>>> teststr('''<python value="not_found"/>''') '<html><body></body></html>'
Use __mode__ for dump exceptions (and see a helpful traceback):
>>> teststr('''<python __mode__="strict" value="not_found"/>''') # fails in IronPython '<html><pre>Traceback (most recent call last):\n File "<string>", line 1, in Element "body"\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></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" __mode__="strict" 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’).
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.1.2 fix encoding handling
0.1.1 updated 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.