Package qm :: Package external :: Package DocumentTemplate :: Module DT_String
[hide private]
[frames] | no frames]

Source Code for Module qm.external.DocumentTemplate.DT_String

  1  ############################################################################## 
  2  # 
  3  # Copyright (c) 2002 Zope Corporation and Contributors. All Rights Reserved. 
  4  # 
  5  # This software is subject to the provisions of the Zope Public License, 
  6  # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution. 
  7  # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED 
  8  # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
  9  # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS 
 10  # FOR A PARTICULAR PURPOSE 
 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 = []  # Create a new marker object. 
 24   
25 -class String:
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 # Document Templates masquerade as functions:
46 - class func_code: pass
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__=()
56 - def errQuote(self, s): return s
57 58 parse_error__roles__=()
59 - def parse_error(self, mess, tag, text, start):
60 raise ParseError, "%s, for tag %s, on line %s of %s" % ( 61 mess, self.errQuote(tag), len(text[:start].split('\n')), 62 self.errQuote(self.__name__))
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__=()
81 - def SubTemplate(self, name):
82 return String('', __name__=name)
83 84 tagre__roles__=()
85 - def tagre(self):
86 return re.compile( 87 '%\\(' # beginning 88 '(?P<name>[a-zA-Z0-9_/.-]+)' # tag name 89 '(' 90 '[\000- ]+' # space after tag name 91 '(?P<args>([^\\)"]+("[^"]*")?)*)' # arguments 92 ')?' 93 '\\)(?P<fmt>[0-9]*[.]?[0-9]*[a-z]|[]![])' # end 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__=()
111 - def parseTag(self, match_ob, command=None, sargs=''):
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 # Waaaaaah! Have to special case else because of 135 # old else start tag usage. Waaaaaaah! 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 # Var command 148 args=args and ("%s %s" % (name, args)) or name 149 return tag, args, Var, None
150 151 varExtra__roles__=()
152 - def varExtra(self, match_ob):
153 return match_ob.group('fmt')
154 155 parse__roles__=()
156 - def parse(self,text,start=0,result=None,tagre=None):
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__=()
188 - def skip_eol(self, text, start, eol=re.compile('[ \t]*\n')):
189 # if block open is followed by newline, then skip past newline 190 mo =eol.match(text,start) 191 if mo is not None: 192 start = start + mo.end(0) - mo.start(0) 193 194 return start
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 # New open tag. Need to find closing tag. 220 start=self.parse_close(text, start, tagre, tag, l, 221 command, args) 222 else: 223 # Either a continuation tag or an end tag 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 # New open tag. Need to find closing tag. 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__=()
286 - def default(self,name=None,**kw):
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__=()
300 - def var(self,name=None,**kw):
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__=()
314 - def munge(self,source_string=None,mapping=None,**vars):
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__=()
325 - def manage_edit(self,data,REQUEST=None):
326 self.munge(data)
327 328 read_raw__roles__=()
329 - def read_raw(self,raw=None):
330 return self.raw
331 332 read__roles__=()
333 - def read(self,raw=None):
334 return self.read_raw()
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__=()
348 - def initvars(self, globals, vars):
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__ = ()
356 - def ZDocumentTemplate_beforeRender(self, md, default):
357 return default
358 359 ZDocumentTemplate_afterRender__roles__ = ()
360 - def ZDocumentTemplate_afterRender(self, md, result):
361 pass
362
363 - def __call__(self,client=None,mapping={},**kw):
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 # print '============================================================' 401 # print '__called__' 402 # print self.raw 403 # print kw 404 # print client 405 # print mapping 406 # print '============================================================' 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 # We were passed a TemplateDict, so we must be a sub-template 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 # if client is a tuple, it represents a "path" of clients 452 # which should be pushed onto the md in order. 453 for ob in client: 454 push(InstanceDict(ob, md)) # Circ. Ref. 8-| 455 pushed=pushed+1 456 else: 457 # otherwise its just a normal client object. 458 push(InstanceDict(client, md)) # Circ. Ref. 8-| 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) # Get rid of circular reference! 480 md.level=level # Restore previous level
481 482 validate__roles__=() 483 validate=None 484
485 - def __str__(self):
486 return self.read()
487
488 - def __getstate__(self, _special=('_v_', '_p_')):
489 # Waaa, we need _v_ behavior but we may not subclass Persistent 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
496 -class FileMixin:
497 # Mix-in class to abstract certain file-related attributes 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__=()
512 - def read_raw(self):
513 if self.edited_source: return self.edited_source 514 if self.raw: return open(self.raw,'r').read() 515 return ''
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