Skip to main content

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="'&lt;p&gt;hello, world&lt;/p&gt;'" 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 "&lt;string&gt;", line 1, in Element "body"\n  File "&lt;string&gt;", line 1, in Element "python"\n  File "&lt;string&gt;", 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>&lt;p&gt;hello, world&lt;/p&gt;</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('''&nbsp;&unknown_entity;''')
'<html><body>&nbsp;&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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

katagami-0.1.2.zip (15.3 kB view details)

Uploaded Source

File details

Details for the file katagami-0.1.2.zip.

File metadata

  • Download URL: katagami-0.1.2.zip
  • Upload date:
  • Size: 15.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No

File hashes

Hashes for katagami-0.1.2.zip
Algorithm Hash digest
SHA256 be93e5e1043d25f6712092db90324244779b09117e376b7de109cbeb65860084
MD5 08f657c7448d52c3e4379b93a45a6541
BLAKE2b-256 4c783fdf75f2875dcf2e8cd164def2a00efab66f77f52a050229b275fc6b783d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page