1
2
3 """
4 This code is taken from the ASPN Python cookbook. I have
5 combined YAPTU and XYAPTU into a single file. The copyright,
6 warranty and license remain with the original authors. Please
7 consult these two URLs
8
9 - U{http://aspn.activestate.com/ASPN/Python/Cookbook/Recipe/52305}
10 - U{http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/162292}
11
12 The following changes were made:
13
14 - Removed very lengthy examples from the end (see the above URLs for
15 them)
16 - Added: setupxcopy() takes the template text and remembers it
17 - Added: xcopywithdns() does the copy with supplied DNS (Document Name Space which is
18 a dict of variables) and remembered template
19 and returns the resulting string
20 - Exception handling for statements was added (xyaptu only did it for
21 expressions)
22 - The default behaviour for exceptions now puts a dump of the exception
23 into the generated output as an HTML <!-- --> style comment so you
24 can view source to find out what has happened. My common library
25 exception formatter (which also dumps local variables) is used.
26
27
28 """
29
30 import sys
31 import re
32 import string
33 import common
34
35
36
37
39 "Polymorphic with a regex that never matches"
42 _never = _nevermatch()
44 "A do-nothing-special-to-the-input, just-return-it function"
45 return string
47 "A do-nothing handler that just re-raises the exception"
48 raise
49
50
52 "Smart-copier (YAPTU) class"
54 "Main copy method: process lines [i,last) of block"
55 def repl(match, self=self):
56 "return the eval of a found expression, for replacement"
57
58 expr = self.preproc(match.group(1), 'eval')
59 try: return common.strorunicode(eval(expr, self.globals, self.locals))
60 except: return common.strorunicode(self.handle(expr))
61 block = self.locals['_bl']
62 if last is None: last = len(block)
63 while i<last:
64 line = block[i]
65 match = self.restat.match(line)
66 if match:
67
68 stat = match.string[match.end(0):].strip()
69 j=i+1
70 nest=1
71 while j<last:
72 line = block[j]
73
74 if self.restend.match(line):
75 nest = nest - 1
76 if nest==0: break
77 elif self.restat.match(line):
78 nest = nest + 1
79 elif nest==1:
80 match = self.recont.match(line)
81 if match:
82 nestat = match.string[match.end(0):].strip()
83 stat = '%s _cb(%s,%s)\n%s' % (stat,i+1,j,nestat)
84 i=j
85 j=j+1
86 stat = self.preproc(stat, 'exec')
87 stat = '%s _cb(%s,%s)' % (stat,i+1,j)
88
89 try:
90 exec stat in self.globals,self.locals
91 except:
92
93 self.ouf.write(str(self.handle(stat,self.locals,self.globals)))
94 i=j+1
95 else:
96 self.ouf.write(self.regex.sub(repl,line))
97 i=i+1
101 "Initialize self's attributes"
102 self.regex = regex
103 self.globals = dict
104 self.locals = { '_cb':self.copyblock }
105 self.restat = restat
106 self.restend = restend
107 self.recont = recont
108 self.preproc = preproc
109 self.handle = handle
110 self.ouf = ouf
111 - def copy(self, block=None, inf=sys.stdin):
112 "Entry point: copy-with-processing a file, or a block of lines"
113 if block is None: block = inf.readlines()
114 self.locals['_bl'] = block
115 self.copyblock()
116
117
118
119
120
121
122
123
124
125
126
127
128
129
131 ' xcopier class, inherits from yaptu.copier '
132
133 - def __init__(self, dns, rExpr=None, rOpen=None, rClose=None, rClause=None,
134 ouf=sys.stdout, dbg=0, dbgOuf=sys.stdout):
135 ' set default regular expressions required by yaptu.copier '
136
137
138
139 _reExpression = re.compile('_:@([^:@]+)@:_')
140 _reOpen = re.compile('\++yaptu ')
141 _reClose = re.compile('--yaptu')
142 _reClause = re.compile('==yaptu ')
143
144 rExpr = rExpr or _reExpression
145 rOpen = rOpen or _reOpen
146 rClose = rClose or _reClose
147 rClause = rClause or _reClause
148
149
150 self.dbg = dbg
151 self.dbgOuf = dbgOuf
152 _preproc = self._preProcess
153 if dbg: _preproc = self._preProcessDbg
154
155
156 copier.__init__(self, rExpr, dns, rOpen, rClose, rClause,
157 preproc=_preproc, handle=self._handleBadExps, ouf=ouf)
158
159
160 - def xcopy(self, inputText):
161 '''
162 Converts the value of the input stream (or contents of input filename)
163 from xyaptu format to yaptu format, and invokes yaptu.copy
164 '''
165
166
167 from StringIO import StringIO
168 yinf = StringIO(self._x2y_translate(inputText))
169 self.copy(inf=yinf)
170 yinf.close()
171
173 from StringIO import StringIO
174 yinf = StringIO(self._x2y_translate(inputText))
175
176
177 self.remembered=[line for line in yinf.readlines()]
178
180 from StringIO import StringIO
181 self.globals=dns
182 out=StringIO()
183 self.ouf=out
184 self.copy(self.remembered)
185 return out.getvalue()
186
188 ' Converts xyaptu markup in input string to yaptu delimeters '
189
190
191
192
193
194
195
196
197 reExpr = re.compile(r'''
198 \$\{([^}]+)\} | # ${py-expr}
199 \$([_\w]+) | # $py-expr
200 <py-expr\s+code\s*=\s*"([^"]*)"\s*/> |
201 <py-expr\s+code\s*=\s*"([^"]*)"\s*>[^<]*</py-expr> |
202 <py-expr\s*>([^<]*)</py-expr\s*>
203 ''', re.VERBOSE)
204
205
206 reLine = re.compile(r'''
207 <py-line\s+code\s*=\s*"([^"]*)"\s*/> |
208 <py-line\s+code\s*=\s*"([^"]*)"\s*>[^<]*</py-line> |
209 <py-line\s*>([^<]*)</py-line\s*>
210 ''', re.VERBOSE)
211
212
213 reOpen = re.compile(r'''
214 <py-open\s+code\s*=\s*"([^"]*)"\s*/> |
215 <py-open\s+code\s*=\s*"([^"]*)"\s*>[^<]*</py-open\s*> |
216 <py-open\s*>([^<]*)</py-open\s*>
217 ''', re.VERBOSE)
218
219
220 reClause = re.compile(r'''
221 <py-clause\s+code\s*=\s*"([^"]*)"\s*/> |
222 <py-clause\s+code\s*=\s*"([^"]*)"\s*>[^<]*</py-clause\s*> |
223 <py-clause\s*>([^<]*)</py-clause\s*>
224 ''', re.VERBOSE)
225
226
227 reClose = re.compile(r'''
228 <py-close\s*/> |
229 <py-close\s*>.*</py-close\s*>
230 ''', re.VERBOSE)
231
232
233
234 def rexpr(match,self=self):
235 return '_:@%s@:_' % match.group(match.lastindex)
236 def rline(match,self=self):
237 return '\n++yaptu %s #\n--yaptu \n' % match.group(match.lastindex)
238 def ropen(match,self=self):
239 return '\n++yaptu %s \n' % match.group(match.lastindex)
240 def rclause(match,self=self):
241 return '\n==yaptu %s \n' % match.group(match.lastindex)
242 def rclose(match,self=self):
243 return '\n--yaptu \n'
244
245
246 xStr = reExpr.sub(rexpr, xStr)
247 xStr = reLine.sub(rline, xStr)
248 xStr = reOpen.sub(ropen, xStr)
249 xStr = reClause.sub(rclause, xStr)
250 xStr = reClose.sub(rclose, xStr)
251
252
253 if self.dbg:
254 _sep = '====================\n'
255 self.dbgOuf.write('%sIntermediate YAPTU format:\n%s\n%s' % (_sep, xStr, _sep))
256
257 return xStr
258
259
261 ' Handle expressions that do not evaluate '
262 if self.dbg:
263 self.dbgOuf.write('!!! ERROR: failed to evaluate expression: %s \n' % s)
264 res="<!-- EXCEPTION: \nExpression: "+s+"\n"
265 res+=common.formatexception()+"\n-->"
266 print common.formatexception()
267 return res+('***! %s !***' % s)
268
269
271 ' Preprocess embedded python statements and expressions '
272 return self._xmlDecode(s)
274 ' Preprocess embedded python statements and expressions '
275 self.dbgOuf.write('!!! DBG: %s %s \n' % (s, why))
276 return self._xmlDecode(s)
277
278
279 _xmlCodes = [
280 ['"', '"'],
281 ['>', '>'],
282 ['<', '<'],
283 ['&', '&'],
284 ]
286 ' Returns the ASCII decoded version of the given HTML string. '
287 codes = self._xmlCodes
288 for code in codes:
289 s = string.replace(s, code[1], code[0])
290 return s
291