1
2
3
4
5
6
7
8
9
10
11
12
13
14 import sys, traceback
15 from cStringIO import StringIO
16 from DT_Util import ParseError, parse_params, render_blocks
17 from DT_Util import namespace, InstanceDict
18 from DT_Return import DTReturn
19
21 """Zope DTML Exception handling
22
23 usage:
24
25 <!--#try-->
26 <!--#except SomeError AnotherError-->
27 <!--#except YetAnotherError-->
28 <!--#except-->
29 <!--#else-->
30 <!--#/try-->
31
32 or:
33
34 <!--#try-->
35 <!--#finally-->
36 <!--#/try-->
37
38 The DTML try tag functions quite like Python's try command.
39
40 The contents of the try tag are rendered. If an exception is raised,
41 then control switches to the except blocks. The first except block to
42 match the type of the error raised is rendered. If an except block has
43 no name then it matches all raised errors.
44
45 The try tag understands class-based exceptions, as well as string-based
46 exceptions. Note: the 'raise' tag raises string-based exceptions.
47
48 Inside the except blocks information about the error is available via
49 three variables.
50
51 'error_type' -- This variable is the name of the exception caught.
52
53 'error_value' -- This is the caught exception's value.
54
55 'error_tb' -- This is a traceback for the caught exception.
56
57 The optional else block is rendered when no exception occurs in the
58 try block. Exceptions in the else block are not handled by the preceding
59 except blocks.
60
61 The try..finally form specifies a `cleanup` block, to be rendered even
62 when an exception occurs. Note that any rendered result is discarded if
63 an exception occurs in either the try or finally blocks. The finally block
64 is only of any use if you need to clean up something that will not be
65 cleaned up by the transaction abort code.
66
67 The finally block will always be called, wether there was an exception in
68 the try block or not, or wether or not you used a return tag in the try
69 block. Note that any output of the finally block is discarded if you use a
70 return tag in the try block.
71
72 If an exception occurs in the try block, and an exception occurs in the
73 finally block, or you use the return tag in that block, any information
74 about that first exception is lost. No information about the first
75 exception is available in the finally block. Also, if you use a return tag
76 in the try block, and an exception occurs in the finally block or you use
77 a return tag there as well, the result returned in the try block will be
78 lost.
79
80 Original version by Jordan B. Baker.
81
82 Try..finally and try..else implementation by Martijn Pieters.
83 """
84
85 name = 'try'
86 blockContinuations = 'except', 'else', 'finally'
87 finallyBlock=None
88 elseBlock=None
89
91 tname, args, section = blocks[0]
92
93 self.args = parse_params(args)
94 self.section = section.blocks
95
96
97
98 if len(blocks) == 2 and blocks[1][0] == 'finally':
99 self.finallyBlock = blocks[1][2].blocks
100
101
102 else:
103
104 self.handlers = []
105 defaultHandlerFound = 0
106
107 for tname,nargs,nsection in blocks[1:]:
108 if tname == 'else':
109 if not self.elseBlock is None:
110 raise ParseError, (
111 'No more than one else block is allowed',
112 self.name)
113 self.elseBlock = nsection.blocks
114
115 elif tname == 'finally':
116 raise ParseError, (
117 'A try..finally combination cannot contain '
118 'any other else, except or finally blocks',
119 self.name)
120
121 else:
122 if not self.elseBlock is None:
123 raise ParseError, (
124 'The else block should be the last block '
125 'in a try tag', self.name)
126
127 for errname in nargs.split():
128 self.handlers.append((errname,nsection.blocks))
129 if nargs.strip()=='':
130 if defaultHandlerFound:
131 raise ParseError, (
132 'Only one default exception handler '
133 'is allowed', self.name)
134 else:
135 defaultHandlerFound = 1
136 self.handlers.append(('',nsection.blocks))
137
143
145 result = ''
146
147
148 try:
149 result = render_blocks(self.section, md)
150 except DTReturn:
151 raise
152 except:
153
154 t,v = sys.exc_info()[:2]
155 if type(t)==type(''):
156 errname = t
157 else:
158 errname = t.__name__
159
160 handler = self.find_handler(t)
161
162 if handler is None:
163
164 raise
165
166
167 try:
168 f=StringIO()
169 traceback.print_exc(100,f)
170 error_tb=f.getvalue()
171 ns = namespace(md, error_type=errname, error_value=v,
172 error_tb=error_tb)[0]
173 md._push(InstanceDict(ns,md))
174 return render_blocks(handler, md)
175 finally:
176 md._pop(1)
177
178 else:
179
180 if (self.elseBlock is None):
181 return result
182 else:
183 return result + render_blocks(self.elseBlock, md)
184
194
196 "recursively search for a handler for a given exception"
197 if type(exception)==type(''):
198 for e,h in self.handlers:
199 if exception==e or e=='':
200 return h
201 else:
202 return None
203 for e,h in self.handlers:
204 if e==exception.__name__ or e=='' or self.match_base(exception,e):
205 return h
206 return None
207
213
214 __call__ = render
215