3 Yet Another Python Templating Utility, Version 1.2, by Alex Martelli.
4 Distributed under PSF license (http://docs.python.org/license.html).
10 # utility stuff to avoid tests in the mainline code
12 "Polymorphic with a regex that never matches"
13 def match(self, line):
15 _never = _nevermatch() # one reusable instance of it suffices
16 def identity(string, why):
17 "A do-nothing-special-to-the-input, just-return-it function"
20 "A do-nothing handler that just re-raises the exception"
23 _default_rex = re.compile('@([^@]+)@')
24 _default_rbe = re.compile('\+\+\+')
25 _default_ren = re.compile('---')
26 _default_rco = re.compile('===')
28 # and now the real thing
30 "Smart-copier (YAPTU) class"
31 def copyblock(self, i=0, last=None):
32 "Main copy method: process lines [i,last) of block"
33 def repl(match, self=self):
34 "return the eval of a found expression, for replacement"
35 # uncomment for debug:
36 # print('!!! replacing',match.group(1))
37 expr = self.preproc(match.group(1), 'eval')
38 try: return str(eval(expr, self.globals, self.locals))
39 except: return str(self.handle(expr))
40 block = self.locals['_bl']
41 if last is None: last = len(block)
44 match = self.restat.match(line)
45 if match: # a statement starts "here" (at line block[i])
46 # i is the last line to _not_ process
47 stat = match.string[match.end(0):].strip()
48 j=i+1 # look for 'finish' from here onwards
49 nest=1 # count nesting levels of statements
52 # first look for nested statements or 'finish' lines
53 if self.restend.match(line): # found a statement-end
54 nest = nest - 1 # update (decrease) nesting
55 if nest==0: break # j is first line to _not_ process
56 elif self.restat.match(line): # found a nested statement
57 nest = nest + 1 # update (increase) nesting
58 elif nest==1: # look for continuation only at this nesting
59 match = self.recont.match(line)
60 if match: # found a contin.-statement
61 nestat = match.string[match.end(0):].strip()
62 stat = '%s _cb(%s,%s)\n%s' % (stat,i+1,j,nestat)
63 i=j # again, i is the last line to _not_ process
65 stat = self.preproc(stat, 'exec')
66 stat = '%s _cb(%s,%s)' % (stat,i+1,j)
67 # for debugging, uncomment...:
68 # print("-> Executing: {"+stat+"}")
69 exec(stat, self.globals, self.locals)
71 else: # normal line, just copy with substitution
72 self.ouf.write(self.regex.sub(repl,line))
74 def __init__(self, dict={}, regex=_default_rex,
75 restat=_default_rbe, restend=_default_ren, recont=_default_rco,
76 preproc=identity, handle=nohandle, ouf=sys.stdout):
77 "Initialize self's attributes"
80 self.locals = { '_cb':self.copyblock }
82 self.restend = restend
84 self.preproc = preproc
87 def copy(self, inf=sys.stdin, block=None):
88 "Entry point: copy-with-processing a file, or a block of lines"
89 if block is None: block = inf.readlines()
90 self.locals['_bl'] = block