1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 __doc__ = """
29 This module provides support for simple but powerful templates.
30 template3k is a template regular language based on substitution of
31 commands for their evaluation.
32
33 There exists two levels of execution:
34
35 Static level (substitution on template compilation time) or dynamic substitution
36 (executed on running time).
37
38 The entire template a python living code, so you can define python classes or
39 functions inside and use them at any time in the template.
40
41 It's based on commands that looks like simple html tags (to be easily used with
42 graphical html design applications). The templates could not be an xml document,
43 but the commands will be named in the same way.
44
45 A template is executed with the method 'merge(values)' where values is a dictionary
46 with values to be used by the commands. Functions and lambdas in dynamic substitutions
47 receives as argument the merge's dictvalues as a dictionary
48
49 Commands:
50
51 python:keyword
52 <python:keyword id="Name" />
53 Replaces the command with values["Name"]
54 <python:keyword id="Name">Default text</python:keyword>
55 Replaces the command with values["Name"], with optionally default text
56
57 python:exec
58 <python:exec id="Name">
59 ...Python Code...
60 </python:exec>
61 Replaces the command with the result of compute the python code inside the command
62 and whose varname is Name.
63 If Name variable is a function or lambda it will be a dynamic substitution
64
65 python:code
66 <python:code id="Name">
67 ...Python Code...
68 </python:code>
69 Replaces the command with an empty string but the code is saved in the environment of
70 the template, so its definitions could be used later
71
72 python:include
73 <python:include id="Name">TemplatePath</python:include>
74 Replaces the command with the template substituion of TemplatePath template. If must exists an special
75 keyword |template_pool| in the merge dictionary to be used to get the template. If this key is not
76 given in the merge, the default module get_template will be used. The arguments in the merge will be
77 passed to this new template merge.
78
79 python:eval
80 <python:eval id="Name">...Python Expression...</python:eval>
81 Replaces the command with the result of the evaluation of the Python Expression inside the command tags.
82 If Name is send to merge it will be replaced for the merge argument.
83 If the result of the expression is a function or a lambda, it will be a dynamic substitution, a string
84 otherwise. If Name is send to merge, the expression will not be executed. There is a implicit argument
85 in the environment named dictvalues, is not needed in the Python Expression, but can be used in the
86 Python Expression as well.
87
88 python:call
89 <python:call id="Name">...Python Expression...</python:call>
90 Replaces the command with the result of the evaluation of the Python Expression inside the command tags.
91 If Name is send to merge it will be replaced for the merge argument.
92 This expression will be always executed. If Name is send to merge, the expression will not be executed.
93
94 """
95
96 import re
97 import warn
98 import os
99
100 _pool = None
101 _template_pattern=re.compile(r"""
102 <python:(?P<command>\w+) \s+
103 id\s*=\s*["'](?P<id>\w+)["']
104 \s*
105 ( (/\>) |
106 (\> (?P<defaultvalue>.*?) </python:(?P<endcommand>\w+)\s*\>) )
107 """, re.IGNORECASE | re.DOTALL | re.MULTILINE | re.VERBOSE )
108
110
113 self.name = name
114 self.defaultvalues = {}
115 self.vars = {}
116 self.template = self._compile_template(data)
117
119 x = d.groupdict()
120 comm = x['command']
121 xid = x['id']
122
123 if comm == 'keyword':
124 if x['defaultvalue'] is None:
125 pass
126 else:
127 self.defaultvalues[xid] = x['defaultvalue']
128 elif comm in ('exec','code'):
129 c = compile(x['defaultvalue'].replace('%%','%')+"\n",self.name + ':id:' + xid,'exec')
130 exec c in self.vars, self.vars
131 if comm == 'code':
132 return ''
133 self.defaultvalues[xid] = self.vars[xid]
134 elif comm == 'eval':
135 self.defaultvalues[xid] = eval(x['defaultvalue'], self.vars, self.vars)
136 elif comm == 'call':
137 self.defaultvalues[xid] = eval("lambda dictvalues: (%s)"%x['defaultvalue'], self.vars, self.vars)
138 elif comm == 'include':
139 def include_template(dictvalues):
140 try:
141 g = dictvalues['template_pool'].get_template
142 except KeyError:
143 g = get_template
144 return g(x['defaultvalue']).merge(dictvalues)
145 self.defaultvalues[xid] = include_template
146 else:
147 raise UnknownTemplateCommand('Unknown Template Command %s'%repr(comm))
148 return '%%(%(id)s)s'%x
149
151 return _template_pattern.sub(self._replace_keywords, re.sub("%","%%", data))
152
153 - def merge(self, dictvalues=None, **kwargs):
154 """ Merge the template with a set of values |dictvalues| """
155 if dictvalues is None:
156 dictvalues = kwargs
157 for k,v in self.defaultvalues.items():
158 try:
159 dictvalues[k]
160 except KeyError:
161 if callable(v):
162 v = v(dictvalues)
163 dictvalues.setdefault(k,v)
164 return self.template%dictvalues
165
168 self.path = path
169 self._CACHE = {}
170
172 _CACHE = self._CACHE
173 filename = os.path.join(self.path,template)
174 try:
175 s = os.stat(filename)
176 except OSError:
177 warn.debug("Template %s don't exists"%repr(filename))
178 raise
179 try:
180 _mtime, _temp = _CACHE[template]
181 if _mtime == s.st_mtime:
182 return _temp
183 except KeyError:
184 pass
185 f = file(filename)
186 data = f.read()
187 f.close()
188 c = Template(filename, data)
189 _CACHE[template] = (s.st_mtime, c)
190 return c
191
197