Add validator support to REQ and has_request_variables.

Validators are similar to converters, but they don't have
to parse JSON, and they are told the name of the request
variable to help format error messages.

(imported from commit 3c33e301892519c67e70675006d5686d9f013353)
This commit is contained in:
Steve Howell 2013-12-13 13:51:18 -05:00
parent 60917c83d6
commit bfb54b5a4d
2 changed files with 50 additions and 1 deletions

View File

@ -332,7 +332,7 @@ class REQ(object):
pass pass
NotSpecified = _NotSpecified() NotSpecified = _NotSpecified()
def __init__(self, whence=None, converter=None, default=NotSpecified): def __init__(self, whence=None, converter=None, default=NotSpecified, validator=None):
""" """
whence: the name of the request variable that should be used whence: the name of the request variable that should be used
for this parameter. Defaults to a request variable of the for this parameter. Defaults to a request variable of the
@ -344,13 +344,21 @@ class REQ(object):
default: a value to be used for the argument if the parameter default: a value to be used for the argument if the parameter
is missing in the request is missing in the request
validator: similar to converter, but takes an already parsed JSON
data structure. If specified, we will parse the JSON request
variable value before passing to the function
""" """
self.post_var_name = whence self.post_var_name = whence
self.func_var_name = None self.func_var_name = None
self.converter = converter self.converter = converter
self.validator = validator
self.default = default self.default = default
if converter and validator:
raise Exception('converter and validator are mutually exclusive')
# Extracts variables from the request object and passes them as # Extracts variables from the request object and passes them as
# named function arguments. The request object must be the first # named function arguments. The request object must be the first
# argument to the function. # argument to the function.
@ -414,6 +422,18 @@ def has_request_variables(view_func):
val = param.converter(val) val = param.converter(val)
except: except:
raise RequestVariableConversionError(param.post_var_name, val) raise RequestVariableConversionError(param.post_var_name, val)
# Validators are like converters, but they don't handle JSON parsing; we do.
if param.validator is not None and not default_assigned:
try:
val = ujson.loads(val)
except:
raise JsonableError('argument "%s" is not valid json.' % (param.post_var_name,))
error = param.validator(param.post_var_name, val)
if error:
raise JsonableError(error)
kwargs[param.func_var_name] = val kwargs[param.func_var_name] = val
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)

View File

@ -226,6 +226,35 @@ class DecoratorTestCase(TestCase):
result = get_total(request) result = get_total(request)
self.assertEqual(result, 21) self.assertEqual(result, 21)
def test_REQ_validator(self):
@has_request_variables
def get_total(request, numbers=REQ(validator=check_list(check_int))):
return sum(numbers)
class Request:
pass
request = Request()
request.REQUEST = {}
with self.assertRaises(RequestVariableMissingError):
get_total(request)
request.REQUEST['numbers'] = 'bad_value'
with self.assertRaises(JsonableError) as cm:
get_total(request)
self.assertEqual(str(cm.exception), 'argument "numbers" is not valid json.')
request.REQUEST['numbers'] = ujson.dumps([1,2,"what?",4,5,6])
with self.assertRaises(JsonableError) as cm:
get_total(request)
self.assertEqual(str(cm.exception), 'numbers[2] is not an integer')
request.REQUEST['numbers'] = ujson.dumps([1,2,3,4,5,6])
result = get_total(request)
self.assertEqual(result, 21)
class ValidatorTestCase(TestCase): class ValidatorTestCase(TestCase):
def test_check_string(self): def test_check_string(self):
x = "hello" x = "hello"