Changeset 1261
- Timestamp:
- 08/21/06 12:40:20
- Files:
-
- trunk/cherrypy/_cprequest.py (modified) (5 diffs)
- trunk/cherrypy/_cptools.py (modified) (2 diffs)
- trunk/cherrypy/lib/cptools.py (modified) (1 diff)
- trunk/cherrypy/lib/sessions.py (modified) (1 diff)
- trunk/cherrypy/test/test_tools.py (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cprequest.py
r1260 r1261 14 14 15 15 class HookMap(object): 16 17 def __init__(self, points=None, failsafe=None): 16 """A map of call points to callback lists. 17 18 callbacks: a dict of the form {call point: [callbacks]}. 19 Each 'call point' is a name and each callback is a callable 20 that takes no arguments. 21 failsafes: a dict of the form {callback: failsafe}. If 'failsafe' is 22 True, the callback is guaranteed to run even if other callbacks 23 from the same call point raise exceptions. False values are 24 permissible, but ignored. 25 """ 26 27 def __init__(self, points=None): 18 28 points = points or [] 19 29 self.callbacks = dict([(point, []) for point in points]) 20 self.failsafe = failsafe or [] 21 22 def attach(self, point, callback, conf=None): 23 if not conf: 24 # No point adding a wrapper if there's no conf 25 self.callbacks[point].append(callback) 26 else: 30 self.failsafes = {} 31 32 def attach(self, point, callback, failsafe=None, **kwargs): 33 """Append callback at the given call point. 34 35 If failsafe is True, the supplied callback is guaranteed to 36 run, even is other callbacks at the same call point fail. 37 If failsafe is None or not given, callback.failsafe will 38 be used if present; otherwise, False is assumed. 39 If additional keyword args are provided, they will be passed 40 to the given callback for each call. 41 """ 42 func = callback 43 if kwargs: 27 44 def wrapper(): 28 callback(**conf) 29 self.callbacks[point].append(wrapper) 45 callback(**kwargs) 46 func = wrapper 47 self.callbacks[point].append(func) 48 if failsafe is None: 49 failsafe = getattr(callback, 'failsafe', False) 50 self.failsafes[func] = failsafe 30 51 31 52 def run(self, point): … … 34 55 raise cherrypy.TimeoutError() 35 56 36 failsafe = point in self.failsafe57 exc = None 37 58 for callback in self.callbacks[point]: 38 59 # Some hookpoints guarantee all callbacks are run even if … … 42 63 # to raise SystemExit and stop the whole server. So, trap 43 64 # your own errors in these callbacks! 44 if failsafe:65 if exc is None or self.failsafes.get(callback, False): 45 66 try: 46 67 callback() … … 48 69 raise 49 70 except: 71 exc = sys.exc_info()[1] 50 72 cherrypy.log(traceback=True) 51 else:52 callback()73 if exc: 74 raise 53 75 54 76 … … 209 231 210 232 self.hooks = HookMap(self.hookpoints) 211 self.hooks.failsafe = ['on_start_resource', 'on_end_resource',212 'on_end_request']213 214 233 self.get_resource(path_info) 215 234 self.tool_up() trunk/cherrypy/_cptools.py
r1235 r1261 93 93 """ 94 94 conf = self._merged_args() 95 cherrypy.request.hooks.attach(self._point, self.callable, conf)95 cherrypy.request.hooks.attach(self._point, self.callable, **conf) 96 96 97 97 … … 133 133 """ 134 134 # Don't pass conf (or our wrapper will get wrapped!) 135 cherrypy.request.hooks.attach(self._point, self._wrapper) 135 f = getattr(self.callable, "failsafe", False) 136 cherrypy.request.hooks.attach(self._point, self._wrapper, failsafe=f) 136 137 137 138 trunk/cherrypy/lib/cptools.py
r1243 r1261 109 109 for name, value in (headers or []): 110 110 cherrypy.response.headers[name] = value 111 response_headers.failsafe = True 111 112 112 113 trunk/cherrypy/lib/sessions.py
r1240 r1261 352 352 # If the session is still locked we release the lock 353 353 sess.release_lock() 354 close.failsafe = True 354 355 355 356 def init(storage_type='ram', path=None, path_header=None, name='session_id', trunk/cherrypy/test/test_tools.py
r1235 r1261 33 33 cherrypy.request.numerify_map = m.items() 34 34 cherrypy.request.hooks.attach('on_start_resource', makemap) 35 36 def critical(): 37 cherrypy.request.error_response = cherrypy.HTTPError(502).set_response 38 critical.failsafe = True 39 cherrypy.request.hooks.attach('on_start_resource', critical) 40 35 41 cherrypy.request.hooks.attach(self._point, self.callable) 42 36 43 tools.numerify = NumTool('before_finalize', numerify) 37 44 … … 43 50 self.ended = {} 44 51 self._name = "nadsat" 45 52 46 53 def nadsat(self): 47 54 def nadsat_it_up(body): … … 56 63 cherrypy.response.body = "razdrez" 57 64 self.ended[cherrypy.request.counter] = True 65 cleanup.failsafe = True 58 66 59 67 def _setup(self): … … 191 199 self.getPage("/demo/err") 192 200 # If body is "razdrez", then on_end_request is being called too early. 193 self.assertErrorPage(50 0, pattern=valerr)201 self.assertErrorPage(502, pattern=valerr) 194 202 # If this fails, then on_end_request isn't being called at all. 195 203 self.getPage("/demo/ended/3") … … 211 219 212 220 def testGuaranteedHooks(self): 213 # The on_start_resource and on_end_request hooks are all 214 # guaranteed to run, even if there are failures in other on_start 215 # or on_end methods. This is NOT true of the other hooks. 216 # Here, we have set up a failure in NumerifyTool.on_start_resource, 217 # but because that failure is logged and passed over, the error 218 # page we obtain in the user agent should be from before_finalize. 221 # The 'critical' on_start_resource hook is 'failsafe' (guaranteed 222 # to run even if there are failures in other on_start methods). 223 # This is NOT true of the other hooks. 224 # Here, we have set up a failure in NumerifyTool.numerify_map, 225 # but our 'critical' hook should run and set the error to 502. 219 226 self.getPage("/demo/err_in_onstart") 220 self.assertErrorPage(500) 221 self.assertInBody("AttributeError: 'Request' object has no " 222 "attribute 'numerify_map'") 227 self.assertErrorPage(502) 228 self.assertInBody("AttributeError: 'str' object has no attribute 'items'") 223 229 224 230 def testCombinedTools(self):

