Finding the source of this problem was incredibly difficult. I'm hoping that this post will serve me in the future when I inevitably come across this problem again.
For the university assessment I'm working on at the moment, I have to use the web2py Python framework to create a web application. Part of the framework allows you to create and process HTML forms, including performing validation on the user's input.
The problem I had today was that I was using a form, and specifically, using the
IS_NOT_IN_DB()
validation function. The issue arose when calling the
process()
method on the form – if the validation failed, then an exception was
being thrown:
Python<type 'exceptions.Exception'> Validation error, field:box-name <validators.IS_NOT_IN_DB object at 0x1125e4990>
Now, I don't know about you, but having an exception which uses the base
Exception
type, and simply dumps a (useless) __repr__()
string as the
message, isn't that useful. In fact, I believed that this was how the validation
was meant to work – if it failed, this exception would be thrown.
That's all great, except after testing my form for a while, I noticed a bug where, if the validation failed, the next submission of the form would simply reload the page and not do anything. Uh oh.
After a lot of debugging using the in-browser web2py debugger (I don't recommend
this), I came across the following code, in gluon/html.py
:
Pythonfor k, validator in enumerate(requires): try: (value, errors) = validator(value) except: msg = "Validation error, field:%s %s" % (name,validator) raise Exception(msg)
I didn't realise it at first, but this code is actually masking any exceptions thrown lower down in the framework during the validation process!
As all good framework debugging should require, I commented out the
try
/except
statement to gain access to the actual exception being thrown,
and – lo and behold – the validator was throwing an exception:
Python<type 'exceptions.TypeError'> 'Rows' object is not callable
The validator was complaining because I had passed it a set of DAL Row
objects, rather than a set of database fields. Modifying the call to
IS_NOT_IN_DB()
and passing it the correct argument worked straight away, and
no new exceptions were thrown.
Maybe the web2py team thought they were helping by masking the exception being thrown, but I don't think they were, particularly. If they hadn't done that, I wouldn't have spent over an hour trying to fix this problem. Maybe from an end-user perspective, an error about validation is nicer than an error about objects not being callable. But I'd argue that no error would be nicer.