Decorator for validation - inefficient?

Bryan bryanvick at gmail.com
Mon Nov 3 16:27:32 CET 2008


Steven D'Aprano wrote:
> On Sun, 02 Nov 2008 09:33:41 -0800, Bryan wrote:
>
> > I'm coming from a .Net background, and yes, one of the reasons I did not
> > consider raising exceptions was to avoid the overhead of an exception
> > handler clause, which in .Net land is expensive.
>
> Actually catching an exception in Python is expensive, so I wouldn't
> recommend you use exceptions for message-passing in time-critical code.
>
> As I understand it, in your case you actually need to stop for user-input
> if the user's data fails the validation. If that's the case, an extra few
> milliseconds to catch an exception isn't going to matter much.
>
>
>
> > some more thought on this:
> >
> > If I were going to be checking for validity during a property setter, I
> > would probably raise an exception there, because the essence of what a
> > client was requesting is "set property", and an invalid value precludes
> > this action from happening.
> >
> > However, hoping to make client code cleaner and to avoid setter
> > functions doing expensive db lookup validations, I do not validate
> > during the setter, but instead defer it until the client explicitly asks
> > for the validity of the business object.
>
> Presumably if they *don't* explicitly ask, you validate anyway at some
> point?
>
>
> > So the essence of the client's
> > request at that point is "what are the invalid values for the object",
> > and an exception should only be raised if there was something stopping
> > this request from being served.  Invalid business object field values do
> > not stop the functionality of the invalid() method.
> >
> > If I had a validation function that checked the db for a duplicate
> > primary key, then the invalid() function should raise an exception if
> > the db could not be contacted.
>
> Yes!
>
>
> > A client should be on the lookout for
> > that type of exception, but to throw a bunch of exceptions back at a
> > client who simply requested a list of things that need to be fixed seems
> > heavy.
> >
> > We would essentially be using Exceptions as an expected return
> > value of a function.  So a doc string would explain: "Returns None for a
> > valid object, and Exceptions for an invalid object."
> >
> > Should exceptions be an expected "return value" from a function?  Am I
> > still using my .Net brain?
>
> Because exceptions are first-class objects just like lists, ints and
> strings, there is a difference between *returning* an exception and
> *raising* an exception.
>
> E.g.:
>
> def build_exception(n):
>     if n < 0:
>         raise ValueError('unexpected negative code')
>     else:
>         exc = TypeError('error code #%d' % n)
>         exc.foo = "More info here"
>         return exc
>
>
> try:
>     obj = build_exception(57)
>     print obj.foo
>     another_obj = build_exception(-1)
>     print "We never get here"
> except ValueError, e:
>     print "Failed with error message:", e.message
>
>
>
> Making the exception part of your code's API is perfectly legitimate and
> I would recommend it. The docstring could say this:
>
> "Return None for a valid object, otherwise raises InvalidDataException
> (subclass of ValueError). You can get a list of errors from the
> exception's errorlist attribute."
>
>
> Here's a minimal way to generate the exception:
>
> class InvalidDataException(ValueError):
>     pass
>
>
> and then in your validation code:
>
> def validate(obj):
>     print "Testing many things here"
>     e = InvalidDataException("failed because of %d errors") % 4
>     e.errorlist = [
>         "too many questions",
>         "too few answers",
>         "not enough respect",
>         "and your query's hair is too long (damn hippy)"]
>     raise e
>
>
> That's one way. There are others. Have a browse through the standard
> library or the Python docs and see how other exceptions are used.
>
>
> But now that I have a better picture of your use-case, I'm leaning
> towards a completely different model. Rather than testing if the business
> object is valid, and raising an error if it isn't, you test it for
> errors, returning an empty list if there aren't any.
>
>
> def check_for_errors(obj):
>     errorlist = []
>     print "Testing many things here"
>     if too_many_questions():
>         errorlist.append("too many questions")
>         # and any others
>     return errorlist
>
>
> I've used strings as errors, but naturally they could be any object that
> makes sense for your application.
>
>
>
> --
> Steven

I like the errorlist attribute on the exception, I think I have a
place for that in my project.  Thanks for all the help, now the next
thing I want to investigate is how I can write my validation code once
and make it work both on my objects, and on webforms that edit those
objects.  I will always have strict validation in the object model
that will be checked on the server side, but it would be convenient
for the user if they were warned before making a round trip to the
server that the values they entered are probably not correct.

This project is using Pylons, so I'm off to investigate the
possibilities.

Bryan



More information about the Python-list mailing list