1
2
3
4
5
6
7
8
9
10
11
12
13 "$Id: DT_String.py 1007 2007-02-10 01:07:28Z stefan $"
14
15 import os
16 import thread
17 import re
18
19 from DT_Util import ParseError, InstanceDict, TemplateDict, render_blocks, str
20 from DT_Var import Var, Call, Comment
21 from DT_Return import ReturnTag, DTReturn
22
23 _marker = []
24
26 """Document templates defined from strings.
27
28 Document template strings use an extended form of python string
29 formatting. To insert a named value, simply include text of the
30 form: '%(name)x', where 'name' is the name of the value and 'x' is
31 a format specification, such as '12.2d'.
32
33 To intrduce a block such as an 'if' or an 'in' or a block continuation,
34 such as an 'else', use '[' as the format specification. To
35 terminate a block, ise ']' as the format specification, as in::
36
37 %(in results)[
38 %(name)s
39 %(in results)]
40
41 """
42
43 isDocTemp=1
44
45
47 func_code=func_code()
48 func_code.co_varnames='self','REQUEST'
49 func_code.co_argcount=2
50 func_code.__roles__=()
51
52 func_defaults__roles__=()
53 func_defaults=()
54
55 errQuote__roles__=()
57
58 parse_error__roles__=()
63
64 commands__roles__=()
65 commands={
66 'var': Var,
67 'call': Call,
68 'in': ('in', 'DT_In','In'),
69 'with': ('with', 'DT_With','With'),
70 'if': ('if', 'DT_If','If'),
71 'unless': ('unless', 'DT_If','Unless'),
72 'else': ('else', 'DT_If','Else'),
73 'comment': Comment,
74 'raise': ('raise', 'DT_Raise','Raise'),
75 'try': ('try', 'DT_Try','Try'),
76 'let': ('let', 'DT_Let', 'Let'),
77 'return': ReturnTag,
78 }
79
80 SubTemplate__roles__=()
83
84 tagre__roles__=()
86 return re.compile(
87 '%\\('
88 '(?P<name>[a-zA-Z0-9_/.-]+)'
89 '('
90 '[\000- ]+'
91 '(?P<args>([^\\)"]+("[^"]*")?)*)'
92 ')?'
93 '\\)(?P<fmt>[0-9]*[.]?[0-9]*[a-z]|[]![])'
94 , re.I)
95
96 _parseTag__roles__=()
97 - def _parseTag(self, match_ob, command=None, sargs='', tt=type(())):
98 tag, args, command, coname = self.parseTag(match_ob,command,sargs)
99 if type(command) is tt:
100 cname, module, name = command
101 d={}
102 try:
103 exec 'from %s import %s' % (module, name) in d
104 except ImportError:
105 exec 'from DocumentTemplate.%s import %s' % (module, name) in d
106 command=d[name]
107 self.commands[cname]=command
108 return tag, args, command, coname
109
110 parseTag__roles__=()
112 """Parse a tag using an already matched re
113
114 Return: tag, args, command, coname
115
116 where: tag is the tag,
117 args is the tag\'s argument string,
118 command is a corresponding command info structure if the
119 tag is a start tag, or None otherwise, and
120 coname is the name of a continue tag (e.g. else)
121 or None otherwise
122 """
123 tag, name, args, fmt = match_ob.group(0, 'name', 'args', 'fmt')
124 args=args and args.strip() or ''
125
126 if fmt==']':
127 if not command or name != command.name:
128 raise ParseError, ('unexpected end tag', tag)
129 return tag, args, None, None
130 elif fmt=='[' or fmt=='!':
131 if command and name in command.blockContinuations:
132
133 if name=='else' and args:
134
135
136 l=len(args)
137 if not (args==sargs or
138 args==sargs[:l] and sargs[l:l+1] in ' \t\n'):
139 return tag, args, self.commands[name], None
140
141 return tag, args, None, name
142
143 try: return tag, args, self.commands[name], None
144 except KeyError:
145 raise ParseError, ('Unexpected tag', tag)
146 else:
147
148 args=args and ("%s %s" % (name, args)) or name
149 return tag, args, Var, None
150
151 varExtra__roles__=()
153 return match_ob.group('fmt')
154
155 parse__roles__=()
157 if result is None: result=[]
158 if tagre is None: tagre=self.tagre()
159 mo = tagre.search(text,start)
160 while mo :
161 l = mo.start(0)
162
163 try: tag, args, command, coname = self._parseTag(mo)
164 except ParseError, m: self.parse_error(m[0],m[1],text,l)
165
166 s=text[start:l]
167 if s: result.append(s)
168 start=l+len(tag)
169
170 if hasattr(command,'blockContinuations'):
171 start=self.parse_block(text, start, result, tagre,
172 tag, l, args, command)
173 else:
174 try:
175 if command is Var: r=command(args, self.varExtra(mo))
176 else: r=command(args)
177 if hasattr(r,'simple_form'): r=r.simple_form
178 result.append(r)
179 except ParseError, m: self.parse_error(m[0],tag,text,l)
180
181 mo = tagre.search(text,start)
182
183 text=text[start:]
184 if text: result.append(text)
185 return result
186
187 skip_eol__roles__=()
195
196 parse_block__roles__=()
197 - def parse_block(self, text, start, result, tagre,
198 stag, sloc, sargs, scommand):
199
200 start=self.skip_eol(text,start)
201
202 blocks=[]
203 tname=scommand.name
204 sname=stag
205 sstart=start
206 sa=sargs
207 while 1:
208
209 mo = tagre.search(text,start)
210 if mo is None: self.parse_error('No closing tag', stag, text, sloc)
211 l = mo.start(0)
212
213 try: tag, args, command, coname= self._parseTag(mo,scommand,sa)
214 except ParseError, m: self.parse_error(m[0],m[1], text, l)
215
216 if command:
217 start=l+len(tag)
218 if hasattr(command, 'blockContinuations'):
219
220 start=self.parse_close(text, start, tagre, tag, l,
221 command, args)
222 else:
223
224 section=self.SubTemplate(sname)
225 section._v_blocks=section.blocks=self.parse(text[:l],sstart)
226 section._v_cooked=None
227 blocks.append((tname,sargs,section))
228
229 start=self.skip_eol(text,l+len(tag))
230
231 if coname:
232 tname=coname
233 sname=tag
234 sargs=args
235 sstart=start
236 else:
237 try:
238 r=scommand(blocks)
239 if hasattr(r,'simple_form'): r=r.simple_form
240 result.append(r)
241 except ParseError, m: self.parse_error(m[0],stag,text,l)
242
243 return start
244
245 parse_close__roles__=()
246 - def parse_close(self, text, start, tagre, stag, sloc, scommand, sa):
247 while 1:
248 mo = tagre.search(text,start)
249 if mo is None: self.parse_error('No closing tag', stag, text, sloc)
250 l = mo.start(0)
251
252 try: tag, args, command, coname= self._parseTag(mo,scommand,sa)
253 except ParseError, m: self.parse_error(m[0],m[1], text, l)
254
255 start=l+len(tag)
256 if command:
257 if hasattr(command, 'blockContinuations'):
258
259 start=self.parse_close(text, start, tagre, tag, l,
260 command,args)
261 elif not coname: return start
262
263 shared_globals__roles__=()
264 shared_globals={}
265
266 - def __init__(self, source_string='', mapping=None, __name__='<string>',
267 **vars):
268 """\
269 Create a document template from a string.
270
271 The optional parameter, 'mapping', may be used to provide a
272 mapping object containing defaults for values to be inserted.
273 """
274 self.raw=source_string
275 self.initvars(mapping, vars)
276 self.setName(__name__)
277
278
279 - def name(self): return self.__name__
280 id=name
281
282 setName__roles__=()
283 - def setName(self,v): self.__dict__['__name__']=v
284
285 default__roles__=()
287 """\
288 Change or query default values in a document template.
289
290 If a name is specified, the value of the named default value
291 before the operation is returned.
292
293 Keyword arguments are used to provide default values.
294 """
295 if name: name=self.globals[name]
296 for key in kw.keys(): self.globals[key]=kw[key]
297 return name
298
299 var__roles__=()
301 """\
302 Change or query a variable in a document template.
303
304 If a name is specified, the value of the named variable before
305 the operation is returned.
306
307 Keyword arguments are used to provide variable values.
308 """
309 if name: name=self._vars[name]
310 for key in kw.keys(): self._vars[key]=kw[key]
311 return name
312
313 munge__roles__=()
315 """\
316 Change the text or default values for a document template.
317 """
318 if mapping is not None or vars:
319 self.initvars(mapping, vars)
320 if source_string is not None:
321 self.raw=source_string
322 self.cook()
323
324 manage_edit__roles__=()
327
328 read_raw__roles__=()
331
332 read__roles__=()
335
336 cook__roles__=()
337 - def cook(self,
338 cooklock=thread.allocate_lock(),
339 ):
340 cooklock.acquire()
341 try:
342 self._v_blocks=self.parse(self.read())
343 self._v_cooked=None
344 finally:
345 cooklock.release()
346
347 initvars__roles__=()
349 if globals:
350 for k in globals.keys():
351 if k[:1] != '_' and not vars.has_key(k): vars[k]=globals[k]
352 self.globals=vars
353 self._vars={}
354
355 ZDocumentTemplate_beforeRender__roles__ = ()
358
359 ZDocumentTemplate_afterRender__roles__ = ()
362
364 '''\
365 Generate a document from a document template.
366
367 The document will be generated by inserting values into the
368 format string specified when the document template was
369 created. Values are inserted using standard python named
370 string formats.
371
372 The optional argument 'client' is used to specify a object
373 containing values to be looked up. Values will be looked up
374 using getattr, so inheritence of values is supported. Note
375 that names beginning with '_' will not be looked up from the
376 client.
377
378 The optional argument, 'mapping' is used to specify a mapping
379 object containing values to be inserted.
380
381 Values to be inserted may also be specified using keyword
382 arguments.
383
384 Values will be inserted from one of several sources. The
385 sources, in the order in which they are consulted, are:
386
387 o Keyword arguments,
388
389 o The 'client' argument,
390
391 o The 'mapping' argument,
392
393 o The keyword arguments provided when the object was
394 created, and
395
396 o The 'mapping' argument provided when the template was
397 created.
398
399 '''
400
401
402
403
404
405
406
407
408 if mapping is None: mapping = {}
409
410 if not hasattr(self,'_v_cooked'):
411 try: changed=self.__changed__()
412 except: changed=1
413 self.cook()
414 if not changed: self.__changed__(0)
415
416 pushed=None
417 try:
418 if mapping.__class__ is TemplateDict: pushed=0
419 except: pass
420
421 globals=self.globals
422 if pushed is not None:
423
424 md=mapping
425 push=md._push
426 if globals:
427 push(self.globals)
428 pushed=pushed+1
429 else:
430 md=TemplateDict()
431 push=md._push
432 shared_globals=self.shared_globals
433 if shared_globals: push(shared_globals)
434 if globals: push(globals)
435 if mapping:
436 push(mapping)
437 md.validate=self.validate
438 if client is not None:
439 if type(client)==type(()):
440 md.this=client[-1]
441 else: md.this=client
442 pushed=0
443
444 level=md.level
445 if level > 200: raise SystemError, (
446 'infinite recursion in document template')
447 md.level=level+1
448
449 if client is not None:
450 if type(client)==type(()):
451
452
453 for ob in client:
454 push(InstanceDict(ob, md))
455 pushed=pushed+1
456 else:
457
458 push(InstanceDict(client, md))
459 pushed=pushed+1
460
461 if self._vars:
462 push(self._vars)
463 pushed=pushed+1
464
465 if kw:
466 push(kw)
467 pushed=pushed+1
468
469 try:
470 value = self.ZDocumentTemplate_beforeRender(md, _marker)
471 if value is _marker:
472 try: result = render_blocks(self._v_blocks, md)
473 except DTReturn, v: result = v.v
474 self.ZDocumentTemplate_afterRender(md, result)
475 return result
476 else:
477 return value
478 finally:
479 if pushed: md._pop(pushed)
480 md.level=level
481
482 validate__roles__=()
483 validate=None
484
487
489
490 d={}
491 for k, v in self.__dict__.items():
492 if k[:3] in _special: continue
493 d[k]=v
494 return d
495
497
498 edited_source=''
499
500 - def __init__(self, file_name='', mapping=None, __name__='', **vars):
501 """\
502 Create a document template based on a named file.
503
504 The optional parameter, 'mapping', may be used to provide a
505 mapping object containing defaults for values to be inserted.
506 """
507 self.raw=file_name
508 self.initvars(mapping, vars)
509 self.setName(__name__ or file_name)
510
511 read_raw__roles__=()
516
517 -class File(FileMixin, String):
518 """\
519 Document templates read from files.
520
521 If the object is pickled, the file name, rather
522 than the file contents is pickled. When the object is
523 unpickled, then the file will be re-read to obtain the string.
524 Note that the file will not be read until the document
525 template is used the first time.
526 """
527 manage_edit__roles__=()
528 - def manage_edit(self,data): raise TypeError, 'cannot edit files'
529