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