"""Monkey-patching and bug-fixes for TurboGears""" import turbogears, sys from turbogears import widgets, validate, util, errorhandling, controllers from turbogears.validators import Invalid ### TG 1.0.4.4 bug in the SelectionField widget ## ##def update_params(self, d): ## """Monkey-patched version of SelectionField's update_params""" ## super(widgets.SelectionField, self).update_params(d) ## grouped_options = [] ## options = [] ## d['options'] = self._extend_options(d['options']) ## for optgroup in d["options"]: ## if isinstance(optgroup[1], list): ## group = True ## optlist = optgroup[1][:] ## else: ## group = False ## optlist = [optgroup] #### import pdb #### pdb.set_trace() ## for i, option in enumerate(optlist): ## if len(option) is 2: ## option_attrs = {} ## elif len(option) is 3: ## option_attrs = dict(option[2]) ## if self._is_option_selected(option[0], d['value']): ## option_attrs[self._selected_verb] = self._selected_verb ## # Here's the fix to let validators use objects while ## # matching simple types... ## value = option[0] ## # HACK: there are controls (such as Wrapper) that asume that ## # '' will be passed as '', whereas there are validators that will ## # convert '' to None which is *wrong* for from_python, it should ## # be the reverse! So we skip null values entirely. ## # That's just plain WRONG! ## # This really needs to be fixed in TurboGears API and we can't ## # easily make a clean fix here, so punting for now. #### if value and hasattr( self.validator, 'from_python' ): #### value = self.validator.from_python( value ) ## optlist[i] = (value, option[1], option_attrs) ## options.extend(optlist) ## if group: ## grouped_options.append((optgroup[0], optlist)) ## # options provides a list of *flat* options leaving out any eventual ## # group, useful for backward compatibility and simpler widgets ## d["options"] = options ## if grouped_options: ## d["grouped_options"] = grouped_options ## else: ## d["grouped_options"] = [(None, options)] ##widgets.SelectionField.update_params = update_params ### TG 1.0.4.4 bugs, collectively the "Validator Bug" ### Discussion at http://groups.google.com/group/turbogears/browse_frm/thread/d8a8af2b60a65714 ### Don't have a full solution in TG, though first fix seems ### to have been integrated into 1.0 trunk as of 2008-04-14 ##def call_on_stack(func_name, kw, start=0): ## """Check if a call to function matching pattern is on stack.""" ## try: ## frame = sys._getframe(start+1) ## except ValueError: ## return False ## ## while frame.f_back: ## frame = frame.f_back ## try: ## if frame.f_code.co_name == func_name: ## args = util.getargvalues(frame)[3] ## for key in kw.iterkeys(): ## try: ## if kw[key] != args[key]: ## break ## except (KeyError, TypeError): ## break ## if key or not args: ## return True ## except StopIteration: ## pass ## return False ##util.call_on_stack = call_on_stack ##errorhandling.call_on_stack = call_on_stack def validate(form=None, validators=None, failsafe_schema=errorhandling.FailsafeSchema.none, failsafe_values=None, state_factory=None): """Validate input. @param form: a form instance that must be passed throught the validation process... you must give a the same form instance as the one that will be used to post data on the controller you are putting the validate decorator on. @type form: a form instance @param validators: individual validators to use for parameters. If you use a schema for validation then the schema instance must be the sole argument. If you use simple validators, then you must pass a dictionnary with each value name to validate as a key of the dictionnary and the validator instance (eg: tg.validators.Int() for integer) as the value. @type validators: dictionnary or schema instance @param failsafe_schema: TODO: complete this docstring fail-safe schema. @type failsafe_schema: ??? @param failsafe_values: TODO: complete this docstring replacements for erroneous inputs @type failsafe_values: ??? @param state_factory: TODO: complete this docstring callable which returns the initial state instance for validation @type state_factory: ??? """ import cherrypy def entangle(func): recursion_guard = dict(func=func) if callable(form) and not hasattr(form, "validate"): init_form = lambda self: form(self) else: init_form = lambda self: form def validate(func, *args, **kw): form = init_form(args and args[0] or kw["self"]) args, kw = util.to_kw(func, args, kw) old_errors = getattr(cherrypy.request,'validation_errors', {}) errors = {} #cherrypy.request.validation_exception = None if state_factory is not None: state = util.state_factory() else: state = None if form: value = kw.copy() try: kw.update(form.validate(value, state)) except Invalid, e: errors.update( e.unpack_errors() ) if not getattr(cherrypy.request,'validation_exception',None): cherrypy.request.validation_exception = e if errors and not old_errors: cherrypy.request.validated_form = form if validators: if isinstance(validators, dict): for field, validator in validators.iteritems(): try: kw[field] = validator.to_python( kw.get(field, None), state ) except Invalid, error: errors[field] = error else: try: value = kw.copy() kw.update(validators.to_python(value, state)) except Invalid, e: errors.update( e.unpack_errors() ) if not getattr(cherrypy.request,'validation_exception',None): cherrypy.request.validation_exception = e cherrypy.request.input_values = kw.copy() cherrypy.request.validation_state = state if errors: cherrypy.request.validation_errors = errors old_errors = errors kw = errorhandling.dispatch_failsafe(failsafe_schema, failsafe_values, errors, func, kw) args, kw = util.from_kw(func, args, kw) # pass original error plus any new ones to the form/widgets old_errors.update( errors ) cherrypy.request.validation_errors = old_errors return errorhandling.run_with_errors(errors, func, *args, **kw) return validate return controllers.weak_signature_decorator(entangle) controllers.validate = validate turbogears.validate = validate