From 16c669784a4f778205d9af57891d2b9d91679595 Mon Sep 17 00:00:00 2001 From: kgriffs Date: Mon, 13 May 2013 15:09:57 -0400 Subject: [PATCH] feat: Add ability to pass a dict 'store' to req.get_param This patch adds an optional 'store' method argument to req.get_param and friends. This makes it easy to construct sparse dicts corresponding to query string params, e.g., for use as kwargs when calling app functions. Closes issue #130 --- falcon/request.py | 36 +++++++++++++++++++++++++------ falcon/tests/test_query_params.py | 30 +++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/falcon/request.py b/falcon/request.py index 23941b2..ba5694c 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -367,13 +367,15 @@ class Request(object): description = 'The "' + name + '" header is required.' raise HTTPBadRequest('Missing header', description) - def get_param(self, name, required=False): + def get_param(self, name, required=False, store=None): """Return the value of a query string parameter as a string Args: name: Parameter name, case-sensitive (e.g., 'sort') required: Set to True to raise HTTPBadRequest instead of returning gracefully when the parameter is not found (default False) + store: A dict-like object in which to place the value of the + param, but only if the param is found. Returns: The value of the param as a string, or None if param is not found @@ -388,6 +390,9 @@ class Request(object): # PERF: Use if..in since it is a good all-around performer; we don't # know how likely params are to be specified by clients. if name in self._params: + if store is not None: + store[name] = self._params[name] + return self._params[name] if not required: @@ -396,7 +401,8 @@ class Request(object): description = 'The "' + name + '" query parameter is required.' raise HTTPBadRequest('Missing query parameter', description) - def get_param_as_int(self, name, required=False, min=None, max=None): + def get_param_as_int(self, name, + required=False, min=None, max=None, store=None): """Return the value of a query string parameter as an int Args: @@ -409,6 +415,8 @@ class Request(object): max: Set to the maximum value allowed for this param. If the param is found and its value is greater than max, an HTTPError is raised. + store: A dict-like object in which to place the value of the + param, but only if the param is found (default None) Returns: The value of the param if it is found and can be converted to an @@ -444,6 +452,9 @@ class Request(object): 'parameter may not exceed %d') % max raise InvalidHeaderValueError(description) + if store is not None: + store[name] = val + return val if not required: @@ -452,7 +463,7 @@ class Request(object): description = 'The "' + name + '" query parameter is required.' raise HTTPBadRequest('Missing query parameter', description) - def get_param_as_bool(self, name, required=False): + def get_param_as_bool(self, name, required=False, store=None): """Return the value of a query string parameter as a boolean Args: @@ -460,6 +471,8 @@ class Request(object): required: Set to True to raise HTTPBadRequest instead of returning gracefully when the parameter is not found or is not one of ['true', 'false'] (default False) + store: A dict-like object in which to place the value of the + param, but only if the param is found (default None) Returns: The value of the param if it is found and can be converted to a @@ -477,21 +490,27 @@ class Request(object): if name in self._params: val = self._params[name] if val == 'true': - return True + val = True elif val == 'false': - return False + val = False else: description = ('The value of the "' + name + '" query ' 'parameter must be "true" or "false".') raise InvalidParamValueError(description) + if store is not None: + store[name] = val + + return val + if not required: return None description = 'The "' + name + '" query parameter is required.' raise HTTPBadRequest('Missing query parameter', description) - def get_param_as_list(self, name, transform=None, required=False): + def get_param_as_list(self, name, + transform=None, required=False, store=None): """Return the value of a query string parameter as a list Note that list items must be comma-separated. @@ -504,6 +523,8 @@ class Request(object): required: Set to True to raise HTTPBadRequest instead of returning gracefully when the parameter is not found or is not an integer (default False) + store: A dict-like object in which to place the value of the + param, but only if the param is found (default None) Returns: The value of the param if it is found. Otherwise, returns None @@ -527,6 +548,9 @@ class Request(object): 'is not formatted correctly.') raise InvalidParamValueError(desc) + if store is not None: + store[name] = items + return items if not required: diff --git a/falcon/tests/test_query_params.py b/falcon/tests/test_query_params.py index abcf826..31e50b1 100644 --- a/falcon/tests/test_query_params.py +++ b/falcon/tests/test_query_params.py @@ -13,8 +13,10 @@ class TestQueryParams(testing.TestBase): self.simulate_request('/', query_string=query_string) req = self.resource.req + store = {} self.assertIs(req.get_param('marker'), None) - self.assertIs(req.get_param('limit'), None) + self.assertIs(req.get_param('limit', store), None) + self.assertNotIn('limit', store) self.assertIs(req.get_param_as_int('limit'), None) self.assertIs(req.get_param_as_bool('limit'), None) self.assertIs(req.get_param_as_list('limit'), None) @@ -26,13 +28,22 @@ class TestQueryParams(testing.TestBase): req = self.resource.req self.assertIs(req.get_param('marker'), None) + store = {} + self.assertIs(req.get_param('marker', store=store), None) + self.assertNotIn('marker', store) + def test_simple(self): query_string = 'marker=deadbeef&limit=25' self.simulate_request('/', query_string=query_string) req = self.resource.req - self.assertEquals(req.get_param('marker') or 'deadbeef', 'deadbeef') - self.assertEquals(req.get_param('limit') or '25', '25') + store = {} + self.assertEquals(req.get_param('marker', store=store) or 'nada', + 'deadbeef') + self.assertEquals(req.get_param('limit', store=store) or '0', '25') + + self.assertEquals(store['marker'], 'deadbeef') + self.assertEquals(store['limit'], '25') def test_allowed_names(self): query_string = ('p=0&p1=23&2p=foo&some-thing=that&blank=&some_thing=x&' @@ -69,8 +80,13 @@ class TestQueryParams(testing.TestBase): req = self.resource.req self.assertRaises(falcon.HTTPBadRequest, req.get_param_as_int, 'marker') + self.assertEquals(req.get_param_as_int('limit'), 25) + store = {} + self.assertEquals(req.get_param_as_int('limit', store=store), 25) + self.assertEquals(store['limit'], 25) + self.assertEquals( req.get_param_as_int('limit', min=1, max=50), 25) @@ -153,6 +169,10 @@ class TestQueryParams(testing.TestBase): self.assertEquals(req.get_param_as_bool('echo'), True) self.assertEquals(req.get_param_as_bool('doit'), False) + store = {} + self.assertEquals(req.get_param_as_bool('echo', store=store), True) + self.assertEquals(store['echo'], True) + def test_list_type(self): query_string = 'colors=red,green,blue&limit=1' self.simulate_request('/', query_string=query_string) @@ -164,6 +184,10 @@ class TestQueryParams(testing.TestBase): self.assertEquals(req.get_param_as_list('limit'), ['1']) self.assertIs(req.get_param_as_list('marker'), None) + store = {} + self.assertEquals(req.get_param_as_list('limit', store=store), ['1']) + self.assertEquals(store['limit'], ['1']) + def test_list_transformer(self): query_string = 'coord=1.4,13,15.1&limit=100' self.simulate_request('/', query_string=query_string)