Package turbogears :: Module errorhandling

Source Code for Module turbogears.errorhandling

  1  """Error handling functions.""" 
  2   
  3  __all__ = ['dispatch_error', 'dispatch_error_adaptor', 'try_call', 
  4     'run_with_errors', 'default', 'register_handler', 'FailsafeSchema', 
  5     'dispatch_failsafe', 'error_handler', 'exception_handler'] 
  6   
  7  import sys 
  8  from itertools import izip, islice 
  9  from inspect import getargspec 
 10   
 11  import cherrypy 
 12  from peak.rules import abstract, when, around, NoApplicableMethods 
 13   
 14  from turbogears.util import (inject_args, adapt_call, call_on_stack, has_arg, 
 15      remove_keys, Enum) 
 16  from turbogears.decorator import func_id 
17 18 19 @abstract() 20 -def dispatch_error(controller, tg_source, 21 tg_errors, tg_exceptions, *args, **kw):
22 """Dispatch error. 23 24 Error handler is a function registered via register_handler or if no 25 such decorator was applied, the method triggering the error. 26 27 """
28 29 # for easier usage and backward compatibility: 30 dispatch_error.when = when.__get__(dispatch_error) 31 dispatch_error.around = around.__get__(dispatch_error)
32 33 @when(dispatch_error, "tg_errors and has_arg(tg_source, 'tg_errors')") 34 -def _register_implicit_errh(controller, tg_source, 35 tg_errors, tg_exceptions, *args, **kw):
36 """Register implicitly declared error handler and re-dispatch. 37 38 Any method declaring tg_errors parameter is considered an implicitly 39 declared error handler. 40 41 """ 42 error_handler(tg_source)(tg_source) 43 return dispatch_error(controller, tg_source, 44 tg_errors, tg_exceptions, *args, **kw)
45
46 @when(dispatch_error, "tg_exceptions and has_arg(tg_source, 'tg_exceptions')") 47 -def _register_implicit_exch(controller, tg_source, 48 tg_errors, tg_exceptions, *args, **kw):
49 """Register implicitly declared exception handler and re-dispatch. 50 51 Any method declaring tg_exceptions parameter is considered an 52 implicitly declared exception handler. 53 54 """ 55 exception_handler(tg_source)(tg_source) 56 return dispatch_error(controller, tg_source, 57 tg_errors, tg_exceptions, *args, **kw)
58
59 -def dispatch_error_adaptor(func):
60 """Construct a signature isomorphic to dispatch_error. 61 62 The actual handler will receive only arguments explicitly 63 declared, and a possible tg_format parameter. 64 65 """ 66 def adaptor(controller, tg_source, 67 tg_errors, tg_exceptions, *args, **kw): 68 tg_format = kw.pop('tg_format', None) 69 args, kw = inject_args(func, {'tg_source': tg_source, 70 'tg_errors': tg_errors, 'tg_exceptions': tg_exceptions}, 71 args, kw, 1) 72 args, kw = adapt_call(func, args, kw, 1) 73 if tg_format is not None: 74 kw['tg_format'] = tg_format 75 return func(controller, *args, **kw)
76 return adaptor 77
78 -def try_call(func, self, *args, **kw):
79 """Call function, catch and dispatch any resulting exception.""" 80 # turbogears.database import here to avoid circular imports 81 from turbogears.database import restart_transaction 82 try: 83 return func(self, *args, **kw) 84 except Exception, e: 85 if (isinstance(e, cherrypy.HTTPRedirect) 86 or call_on_stack('dispatch_error', 87 {'tg_source': func, 'tg_exception': e}, 4)): 88 raise 89 else: 90 exc_type, exc_value, exc_trace = sys.exc_info() 91 remove_keys(kw, ('tg_source', 'tg_errors', 'tg_exceptions')) 92 if 'tg_format' in cherrypy.request.params: 93 kw['tg_format'] = 'json' 94 if getattr(cherrypy.request, 'in_transaction', None): 95 restart_transaction(1) 96 try: 97 output = dispatch_error(self, func, None, e, *args, **kw) 98 except NoApplicableMethods: 99 raise exc_type, exc_value, exc_trace 100 else: 101 del exc_trace 102 return output
103
104 -def run_with_errors(errors, func, self, *args, **kw):
105 """Branch execution depending on presence of errors.""" 106 if errors: 107 if hasattr(self, 'validation_error'): 108 import warnings 109 warnings.warn( 110 "Use decorator error_handler() on per-method base " 111 "rather than defining a validation_error() method.", 112 DeprecationWarning, 2) 113 return self.validation_error(func.__name__, kw, errors) 114 else: 115 remove_keys(kw, ('tg_source', 'tg_errors', 'tg_exceptions')) 116 if 'tg_format' in cherrypy.request.params: 117 kw['tg_format'] = 'json' 118 try: 119 return dispatch_error(self, func, errors, None, *args, **kw) 120 except NoApplicableMethods: 121 raise NotImplementedError("Method %s.%s() has no applicable " 122 "error handler." % (self.__class__.__name__, func.__name__)) 123 else: 124 return func(self, *args, **kw)
125
126 -def register_handler(handler=None, rules=None):
127 """Register handler as an error handler for decorated method. 128 129 If handler is not given, method is considered its own error handler. 130 131 rules can be a string containing an arbitrary logical Python expression 132 to be used as dispatch rule allowing multiple error handlers for a 133 single method. 134 135 register_handler decorator is an invariant. 136 137 """ 138 def register(func): 139 new_rules = "func_id(tg_source) == %d" % func_id(func) 140 if rules: 141 new_rules += " and (%s)" % rules 142 around(dispatch_error, new_rules)( 143 dispatch_error_adaptor(handler or func)) 144 return func
145 return register 146
147 -def bind_rules(pre_rules):
148 """Prepend rules to error handler specialisation.""" 149 def registrant(handler=None, rules=None): 150 new_rules = pre_rules 151 if rules: 152 new_rules += " and (%s)" % rules 153 return register_handler(handler, new_rules)
154 return registrant 155 156 error_handler = bind_rules('tg_errors') 157 exception_handler = bind_rules('tg_exceptions') 158 159 160 FailsafeSchema = Enum('none', 'values', 'map_errors', 'defaults')
161 162 -def dispatch_failsafe(schema, values, errors, source, kw):
163 """Dispatch fail-safe mechanism for failed inputs.""" 164 return kw
165 166 @when(dispatch_failsafe, "schema is FailsafeSchema.values" 167 " and isinstance(values, dict) and isinstance(errors, dict)")
168 -def _failsafe_values_dict(schema, values, errors, source, kw):
169 """Map erroneous inputs to values.""" 170 for key in errors: 171 if key in values: 172 kw[key] = values[key] 173 return kw
174 175 @when(dispatch_failsafe, 176 "schema is FailsafeSchema.values and isinstance(errors, dict)")
177 -def _failsafe_values_atom(schema, values, errors, source, kw):
178 """Map all erroneous inputs to a single value.""" 179 for key in errors: 180 kw[key] = values 181 return kw
182 183 @when(dispatch_failsafe, 184 "schema is FailsafeSchema.map_errors and isinstance(errors, dict)")
185 -def _failsafe_map_errors(schema, values, errors, source, kw):
186 """Map erroneous inputs to corresponding exceptions.""" 187 kw.update(errors) 188 return kw
189 190 @when(dispatch_failsafe, 191 "schema is FailsafeSchema.defaults and isinstance(errors, dict)")
192 -def _failsafe_defaults(schema, values, errors, source, kw):
193 """Map erroneous inputs to method defaults.""" 194 argnames, defaultvals = getargspec(source)[::3] 195 defaults = dict(izip(islice( 196 argnames, len(argnames) - len(defaultvals), None), defaultvals)) 197 for key in errors: 198 if key in defaults: 199 kw[key] = defaults[key] 200 return kw
201