Package turbogears :: Module errorhandling

Source Code for Module turbogears.errorhandling

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