]> git.llucax.com Git - z.facultad/75.00/presentacion.git/blob - templite.py
Borrar notas.pdf en make clean-all
[z.facultad/75.00/presentacion.git] / templite.py
1 #!/usr/bin/env python
2 #
3 #       Templite+
4 #       A light-weight, fully functional, general purpose templating engine
5 #
6 #       Copyright (c) 2009 joonis new media
7 #       Author: Thimo Kraemer <thimo.kraemer@joonis.de>
8 #
9 #       Based on Templite - Tomer Filiba
10 #       http://code.activestate.com/recipes/496702/
11 #
12 #       This program is free software; you can redistribute it and/or modify
13 #       it under the terms of the GNU General Public License as published by
14 #       the Free Software Foundation; either version 2 of the License, or
15 #       (at your option) any later version.
16 #       
17 #       This program is distributed in the hope that it will be useful,
18 #       but WITHOUT ANY WARRANTY; without even the implied warranty of
19 #       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 #       GNU General Public License for more details.
21 #       
22 #       You should have received a copy of the GNU General Public License
23 #       along with this program; if not, write to the Free Software
24 #       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 #       MA 02110-1301, USA.
26 #
27
28 import sys, re
29
30 class Templite(object):
31     auto_emit = re.compile('(^[\'\"])|(^[a-zA-Z0-9_\[\]\'\"]+$)')
32
33     def __init__(self, template, start='${', end='}$'):
34         if len(start) != 2 or len(end) != 2:
35             raise ValueError('each delimiter must be two characters long')
36         delimiter = re.compile('%s(.*?)%s' % (re.escape(start), re.escape(end)), re.DOTALL)
37         offset = 0
38         tokens = []
39         for i, part in enumerate(delimiter.split(template)):
40             part = part.replace('\\'.join(list(start)), start)
41             part = part.replace('\\'.join(list(end)), end)
42             if i % 2 == 0:
43                 if not part: continue
44                 part = part.replace('\\', '\\\\').replace('"', '\\"')
45                 part = '\t' * offset + 'emit("""%s""")' % part
46             else:
47                 part = part.rstrip()
48                 if not part: continue
49                 if part.lstrip().startswith(':'):
50                     if not offset:
51                         raise SyntaxError('no block statement to terminate: ${%s}$' % part)
52                     offset -= 1
53                     part = part.lstrip()[1:]
54                     if not part.endswith(':'): continue
55                 elif self.auto_emit.match(part.lstrip()):
56                     part = 'emit(%s)' % part.lstrip()
57                 lines = part.splitlines()
58                 margin = min(len(l) - len(l.lstrip()) for l in lines if l.strip())
59                 part = '\n'.join('\t' * offset + l[margin:] for l in lines)
60                 if part.endswith(':'):
61                     offset += 1
62             tokens.append(part)
63         if offset:
64             raise SyntaxError('%i block statement(s) not terminated' % offset)
65         self.__code = compile('\n'.join(tokens), '<templite %r>' % template[:20], 'exec')
66
67     def render(self, __namespace=None, **kw):
68         """
69         renders the template according to the given namespace.
70         __namespace - a dictionary serving as a namespace for evaluation
71         **kw - keyword arguments which are added to the namespace
72         """
73         namespace = {}
74         if __namespace: namespace.update(__namespace)
75         if kw: namespace.update(kw)
76         namespace['emit'] = self.write
77
78         __stdout = sys.stdout
79         sys.stdout = self
80         self.__output = []
81         eval(self.__code, namespace)
82         sys.stdout = __stdout
83         return ''.join(self.__output)
84
85     def write(self, *args):
86         for a in args:
87             self.__output.append(str(a))
88
89
90 if __name__ == '__main__':
91
92     if '--demo' not in sys.argv:
93         vars = eval('dict(' + ' '.join(sys.argv[1:]) + ')')
94         sys.stdout.write(Templite(sys.stdin.read()).render(vars))
95         sys.exit(0)
96
97     template = r"""
98 This we already know:
99 <html>
100     <body>
101         ${
102         def say_hello(arg):
103             emit("hello ", arg, "<br>")
104         }$
105
106         <table>
107             ${
108                 for i in range(10):
109                     emit("<tr><td> ")
110                     say_hello(i)
111                     emit(" </tr></td>\n")
112             }$
113         </table>
114
115         ${emit("hi")}$
116
117         tralala ${if x > 7:
118             say_hello("big x")}$ lala
119
120         $\{this is escaped starting delimiter
121
122         ${emit("this }\$ is an escaped ending delimiter")}$
123
124         ${# this is a python comment }$
125
126     </body>
127 </html>
128
129 But this is completely new:
130 ${if x > 7:}$
131     x is ${emit('greater')}$ than ${print x-1}$ Well, the print statement produces a newline.
132 ${:else:}$
133  This terminates the previous code block and starts an else code block
134  Also this would work: $\{:end}\$$\{else:}\$, but not this: $\{:end}\$ $\{else:}\$
135 ${:this terminates the else-block
136 only the starting colon is essential}$
137
138 So far you had to write:
139 ${
140     if x > 3:
141         emit('''
142             After a condition you could not continue your template.
143             You had to write pure python code.
144             The only way was to use %%-based substitutions %s
145             ''' % x)
146 }$
147
148 ${if x > 6:}$
149     Now you do not need to break your template ${print x}$
150 ${:elif x > 3:}$
151     This is great
152 ${:endif}$
153
154 ${for i in range(x-1):}$  Of course you can use any type of block statement ${i}$ ${"fmt: %s" % (i*2)}$
155 ${:else:}$
156 Single variables and expressions starting with quotes are substituted automatically.
157 Instead $\{emit(x)}\$ you can write $\{x}\$ or $\{'%s' % x}\$ or $\{"", x}\$
158 Therefore standalone statements like break, continue or pass
159 must be enlosed by a semicolon: $\{continue;}\$
160 The end
161 ${:end-for}$
162 """
163
164     t = Templite(template)
165     print t.render(x=8)
166
167
168     # Output is:
169     """
170 This we already know:
171 <html>
172     <body>
173
174
175         <table>
176             <tr><td> hello 0<br> </tr></td>
177 <tr><td> hello 1<br> </tr></td>
178 <tr><td> hello 2<br> </tr></td>
179 <tr><td> hello 3<br> </tr></td>
180 <tr><td> hello 4<br> </tr></td>
181 <tr><td> hello 5<br> </tr></td>
182 <tr><td> hello 6<br> </tr></td>
183 <tr><td> hello 7<br> </tr></td>
184 <tr><td> hello 8<br> </tr></td>
185 <tr><td> hello 9<br> </tr></td>
186
187         </table>
188
189         hi
190
191         tralala hello big x<br> lala
192
193         ${this is escaped starting delimiter
194
195         this }$ is an escaped ending delimiter
196
197
198
199     </body>
200 </html>
201
202 But this is completely new:
203
204     x is greater than 7
205  Well, the print statement produces a newline.
206
207
208 So far you had to write:
209
210         After a condition you could not continue your template.
211         You had to write pure python code.
212         The only way was to use %-based substitutions 8
213
214
215
216     Now you do not need to break your template 8
217
218
219
220   Of course you can use any type of block statement 0 fmt: 0
221   Of course you can use any type of block statement 1 fmt: 2
222   Of course you can use any type of block statement 2 fmt: 4
223   Of course you can use any type of block statement 3 fmt: 6
224   Of course you can use any type of block statement 4 fmt: 8
225   Of course you can use any type of block statement 5 fmt: 10
226   Of course you can use any type of block statement 6 fmt: 12
227
228 Single variables and expressions starting with quotes are substituted automatically.
229 Instead ${emit(x)}$ you can write ${x}$ or ${'%s' % x}$ or ${"", x}$
230 Therefore standalone statements like break, continue or pass
231 must be enlosed by a semicolon: ${continue;}$
232 The end
233 """
234