Skip to content

Instantly share code, notes, and snippets.

@raphigaziano
Last active December 16, 2015 11:59
Show Gist options
  • Save raphigaziano/5431016 to your computer and use it in GitHub Desktop.
Save raphigaziano/5431016 to your computer and use it in GitHub Desktop.
Hello, my name is {{ name }}!
I'm {{ age }} years old, yay!
#!/usr/bin/env python
#-*- coding:utf-8 -*-
'''
Testing for a simple template ENGINE.
Tested: py2.7, py3.2 on win7
Author: raphi <r.gaziano@gmail.com>
Created: 18/04/2013
Version: 0.1
'''
from __future__ import print_function
from __future__ import unicode_literals
import sys, os
import re
import logging
# DEBUG IMPORTS #
from pprint import pprint; from pdb import set_trace
if sys.version > '3':
unicode = str
_logger = logging.getLogger(__name__)
#~ _logger.addHandler(logging.NullHandler())
_logger.addHandler(logging.StreamHandler())
_logger.setLevel(logging.INFO)
# <external module>
### Regex Helpers ###
#####################
# ...
### Default Callbacks Processors ###
####################################
def sub(m, **kwargs):
tag = m.group(1) if m.groups() else m.group()
val = str( kwargs.get(tag, "") ) # notenough... default err string ?
_logger.info("%s\t=> %s" % (tag, val))
return val
def comment(m, **kwargs):
#~ print("Processing comments: %s" % m.group(0))
return '' # avoid empty lines?
def include(m, **kwargs):
# if "in memory templs" can be registered somewhere, then
# we could name them and include them as well...
# but for now only works with a file path
path = m.group(1)
t = Template(path)
return t(**kwargs)
# ...
# </external module>
class Tag(object):
""" Class doc """
def __init__ (self, regexp, f=sub):
""" Class initialiser """
self.regexp = re.compile(regexp)
self.process = f
def render(self, tmpl, **kwargs):
''' '''
out = tmpl
#~ _logger.info("Subs for %s..." % self)
#~ for m in re.finditer(self.pattern, tmpl):
for m in self.finditer(tmpl):
sub_pattern = re.escape(m.group(0))
out = re.sub(sub_pattern, self.process(m, **kwargs), out)
return out
def __repr__(self):
return "<Tag> %s" % self.regexp.pattern
# <???>
def __getattr__(self, attr): return getattr(self.regexp, attr)
# </???>
# Rather useless for now...
class _TagManager(object):
""" """
tags = []
@classmethod
def add(cls, p):
""" """
cls.tags.append(p)
@classmethod
def clear(cls):
""" """
cls.tags = []
class Template(object):
""" """
def __init__(self, tmpl):
""" """
if os.path.exists(tmpl):
tmpl = open(tmpl, 'r').read()
# unicode checks?
self.tmpl = tmpl
def __call__(self, *args, **kwargs):
# instead of a render method ?
# idea from pyratemp
out = self.tmpl
for t in _TagManager.tags:
out = t.render(out, **kwargs)
return out
### Top Level, Convenience functions ###
########################################
def register(*tags):
""" """
for t in tags:
#~ print("registering tag %s, %s instance" % (t, type(t)))
if isinstance(t, unicode):
_TagManager.add(Tag(t))
elif isinstance(t, Tag):
_TagManager.add(t)
#~ print("Registered tags: %s" % ", ".join([str(t)
#~ for t in _TagManager.tags]))
def render(tmpl, **kwargs):
"""Not used, kept for tests"""
out = tmpl
for p in _TagManager.tags:
_logger.info("Subs for %s..." % p)
for m in re.finditer(p.pattern, tmpl):
tag = m.group(1) if m.groups() else m.group()
val = str( kwargs.get(tag, "") ) # notenough... default err string ?
_logger.info("%s => %s" % (tag, val))
# 2nd arg can be a func acting on the match obj...
out = re.sub(m.group(0), val, out)
return out
### Pseudo Testing ###
######################
if __name__ == '__main__':
register(
"popo",
"papa",
"pipi"
)
def testing(s):
hashes = '#' * (9 + len(s))
print("\n%s\n%s\n%s\n" % (hashes, "Testing: " + s, hashes))
testing("Tag Registration and Retrieval")
from pprint import pprint
pprint(_TagManager.tags)
testing("Stoopid, Individual Tag sub")
for t in _TagManager.tags:
tag = t.regexp.pattern
print("%s => %s" % (t, t.regexp.sub("pwal", "test_%s" % tag)))
_TagManager.clear()
assert(_TagManager.tags == [])
register(
"_name",
"_age"
)
testing("Simple Text Processing")
tmpl = """
Hello, my name is _name!
I'm _age years old, yay!
"""
print(render(tmpl, _name="raphi", _age="26"))
testing("Simple Text Processing - repated tags")
tmpl = """
Hello, my name is _name!
I'm _age years old, yay!
Did i say my name was _name?
"""
print(render(tmpl, _name="raphi", _age="26"))
_TagManager.clear()
#~ testing("Verbose tag definition")
_TagManager.clear()
register(
'{{ ?(?P<tag>\w+) ?}}'
#~ '{{ ?(\w+) ?}}'
)
testing("Moar \"complex\" regex (single group)")
tmpl = """
Hello, my name is {{ name }}!
I'm {{ age }} years old, yay!
"""
print(render(tmpl, name="bob", age="32"))
testing("Missing var => blank (tempo: should raise a warning or fail)")
print(render(tmpl, name="marylou"))
testing("Int converted to Str")
print(render(tmpl, name="Joe", age=17))
testing("Template Object rendering")
tmpl = Template("""
Hello, my name is {{ name}}!
I'm {{ age }} years old, yay!
"""
)
print(tmpl(name="Bill", age=78))
testing("Registering Tag instances directly")
_TagManager.clear()
register(
Tag("{{ ?(\w+) ?}}")
)
print("Ok")
testing("Custom Tag Function - no args")
def custom_func(m, **kwargs):
return sub(m, customtag="custom func called")
t = Tag("customtag", custom_func)
register(t)
print(Template("test test customtag test")())
_TagManager.tags.remove(t)
testing("Custom Tag Function - lambda")
t = Tag("customtag",
lambda m, **kwargs: \
sub(m, customtag="lambda custom func called")
)
register(t)
print(Template("test customtag test customtag test")())
_TagManager.tags.remove(t)
#~ Tag.process = basic_substitute # Onoes.... custom funcs overides default one
testing("Load Template from File")
t = Template("tmpl.txt")
print(t(name="zoulou", age=65))
testing("Comment Tag")
# (?s): flag to make . match newlines
#~ register(Tag(r"""(?xs) # flags: verbose, dotall
#~ {\# # start tag
#~ (.*?) # any character, anytime (including newlines)
#~ \#} # end tag
#~ """, comment))
register(Tag(r"(?s){#(.*)#}", comment))
tmpl = Template("""
{# This is a comment #}
Hello, my name is {{ name}}!
I'm {{ age }} years old, yay!
"""
)
print(tmpl(name="suzy", age=3))
testing("Multiline Comment")
tmpl = Template("""
{# This is
a multiline
comment #}
Hello, my name is {{ name}}!
I'm {{ age }} years old, yay!
"""
)
print(tmpl(name="lilly", age=33))
testing("Include Tag")
register(Tag(r"(?s){%\s?include\((.*)\)\s?%}", include))
tmpl = Template("""
{{ title }}
In base template
{% include(tmpl.txt) %}
Back in base tmpl
"""
)
print(tmpl(title="INCLUDE TEST", name="jojo", age=98))
# TEST TAG __getattr__ !
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment