request: Add new str_validator validator type.

This is helpful for cases where an argument is supposed to be a normal
string, and we want to use a Zulip validator function to do basic
things like check its length.
This commit is contained in:
Tim Abbott 2018-05-03 15:22:00 -07:00
parent f2e84f25a0
commit 51517fa188
3 changed files with 40 additions and 1 deletions

View File

@ -50,6 +50,7 @@ class REQ:
def __init__(self, whence: str=None, *, converter: Callable[[Any], Any]=None,
default: Any=NotSpecified, validator: Callable[[Any], Any]=None,
str_validator: Callable[[Any], Any]=None,
argument_type: str=None, type: Type=None) -> None:
"""whence: the name of the request variable that should be used
for this parameter. Defaults to a request variable of the
@ -66,6 +67,8 @@ class REQ:
data structure. If specified, we will parse the JSON request
variable value before passing to the function
str_validator: Like validator, but doesn't parse JSON first.
argument_type: pass 'body' to extract the parsed JSON
corresponding to the request body
@ -78,12 +81,16 @@ class REQ:
self.func_var_name = None # type: str
self.converter = converter
self.validator = validator
self.str_validator = str_validator
self.default = default
self.argument_type = argument_type
if converter and validator:
if converter and (validator or str_validator):
# Not user-facing, so shouldn't be tagged for translation
raise AssertionError('converter and validator are mutually exclusive')
if validator and str_validator:
# Not user-facing, so shouldn't be tagged for translation
raise AssertionError('validator and str_validator are mutually exclusive')
# Extracts variables from the request object and passes them as
# named function arguments. The request object must be the first
@ -169,6 +176,12 @@ def has_request_variables(view_func):
if error:
raise JsonableError(error)
# str_validators is like validator, but for direct strings (no JSON parsing).
if param.str_validator is not None and not default_assigned:
error = param.str_validator(param.post_var_name, val)
if error:
raise JsonableError(error)
kwargs[param.func_var_name] = val
return view_func(request, *args, **kwargs)

View File

@ -25,6 +25,7 @@ def REQ(whence: Optional[str] = None,
converter: Optional[Callable[[str], ResultT]] = None,
default: Union[_NotSpecified, ResultT] = NotSpecified,
validator: Optional[Validator] = None,
str_validator: Optional[Validator] = None,
argument_type: Optional[str] = None) -> ResultT: ...
def has_request_variables(view_func: ViewFuncT) -> ViewFuncT: ...

View File

@ -181,6 +181,31 @@ class DecoratorTestCase(TestCase):
result = get_total(request)
self.assertEqual(result, 21)
def test_REQ_str_validator(self) -> None:
@has_request_variables
def get_middle_characters(request: HttpRequest,
value: str=REQ(str_validator=check_string_fixed_length(5))) -> str:
return value[1:-1]
class Request:
GET = {} # type: Dict[str, str]
POST = {} # type: Dict[str, str]
request = Request()
with self.assertRaises(RequestVariableMissingError):
get_middle_characters(request)
request.POST['value'] = 'long_value'
with self.assertRaises(JsonableError) as cm:
get_middle_characters(request)
self.assertEqual(str(cm.exception), 'value has incorrect length 10; should be 5')
request.POST['value'] = 'valid'
result = get_middle_characters(request)
self.assertEqual(result, 'ali')
def test_REQ_argument_type(self) -> None:
@has_request_variables
def get_payload(request: HttpRequest,