On Feb 27, 2006, at 3:11 PM, David Binger wrote: > On Feb 27, 2006, at 2:39 AM, mario ruggier wrote: > >> I of course meant something like this > > I may have lost track of what it is that your suggested > change fixes. > > If I remember correctly, this is just about wanting more > detailed error explanations when an "both" spec is not > matched. Eh, no. I do not really care about detailed error explanations... except while developing of course. What I care about is the possibility to associate a spec to something, such as a form field, and to that spec attach (optionally) 2 messages, one for OK and one for Error. And those messages are not at all concerned with underlying exceptions, but are human-application interface messages, to tell for example a user in plain english, or in french, or german, that a space character in her userid is verboten. Those messages cannot be coming from within spec classes, but must be necessarily part of the application-specific logic. So, just for the sake of a concrete example: form.add_password('passwd1', title="Password Confirm", required=False, hint="Case sensitive. At least 4 chars long. At least 1 digit.", spec=spec(both( spec(equalwidget(form, 'passwd2'), 'not equal.'), spec(pattern('^\d|^\D+\d'), 'at least 1 digit.'), spec(length(4, None), 'too short.')), 'Password is not OK... ', 'Password is OK.')) That will give back the following errors according to which spec failed: Password is not OK... not equal. Password is not OK... at least 1 digit. Password is not OK... too short. Or, on success: Password is OK. And, this it does both client side prior to submit, as well as server-side... more on that some other time. Anyhow, I thought I could either go and subclass every spec class that I use to allow such messages per spec instance, or wrap something around the existing spec classes. I opted for this second option (also for the reason that not all specs are instances of SpecOperator) and thus I have a wrapper class (used in the example above) whose init looks like this: class SpecWithMessages (qp.lib.spec.Specification): def __init__(self, spec, error_message=None, ok_message=None, doc='') ... The client code using specs wrapped in this this way can then set its own err and ok messages as it pleases and in the language of the user. And, all it needs to know to be able to get to those messages is which spec instance failed or succeeded. In the case of normal single SpecOperator, this is anyway always known, but in the case of ConnectiveSpecOperators I cannot currently know which sub-spec caused the failure or the success of the spec. The Spec.explain_difference(value) pattern you suggest below is for sure useful for error details from a developer's point of view, but does feel right as the solution for this, even if I could use/coerce it for it, thus for "both" the method i would want would simply become: def explain_difference(self, value): for spec in self.specs: if not match(value, spec): return spec which seems pretty silly to me to go thru that (again) to just get which spec had just previously failed. Besides, it would anyway still require to extend the class, as the intended use of explain_difference() is not this. . Anyway this issue concerns only compound specs, and how to know which sub-spec gave failure or gave success. mario > It isn't clear to me that that is really necessary, > but if it is, I think it could be dealt with more directly > instead of imposing change on all of these other classes. > > =================================================================== > --- lib/spec.py (revision 27998) > +++ lib/spec.py (working copy) > @@ -52,6 +52,9 @@ > def __str__(self): > return "%s(%s)" % (self.__class__.__name__, > self.format_args()) > + def explain_difference(self, value): > + return format_expected_got(self, value) > + > def match(value, spec): > """ > Return True or False depending on whether or not value matches > @@ -141,10 +144,16 @@ > pass > return False > +def format_expected_got(value, spec): > + return ('\n Expected: %s\n' > + ' Got: %r\n') % (format_spec(spec), value) > + > def require(value, spec, message=None): > if not match(value, spec): > - error = ('\n Expected: %s\n' > - ' Got: %r\n') % (format_spec(spec), value) > + if isinstance(spec, SpecOperator): > + error = spec.explain_difference(value) > + else: > + error = format_expected_got(spec, value) > if message: > error = '(%s)%s' % (message, error) > raise TypeError(error) > @@ -272,6 +281,13 @@ > return False > return True > + def explain_difference(self, value): > + error = format_expected_got(value, self) > + for spec in self.specs: > + if not match(value, spec): > + error += " (which does not match %s)" % > format_spec(spec) > + break > + return error > > > > > _______________________________________________ > QP mailing list > QP@mems-exchange.org > http://mail.mems-exchange.org/mailman/listinfo/qp