diff --git a/falcon/request.py b/falcon/request.py index 73c767d..8dff362 100644 --- a/falcon/request.py +++ b/falcon/request.py @@ -549,7 +549,9 @@ class Request(object): name: Parameter name, case-sensitive (e.g., 'limit') transform: An optional transform function that takes as input each element in the list as a string and outputs a transformed - element for inclusion in the list that will be returned. + element for inclusion in the list that will be returned. For + example, passing the int function will transform list items + into numbers. required: Set to True to raise HTTPBadRequest instead of returning gracefully when the parameter is not found or is not an integer (default False) @@ -558,21 +560,43 @@ class Request(object): Returns: The value of the param if it is found. Otherwise, returns None - unless required is True. + unless required is True. for partial lists, None will be returned + as a placeholder. For example: + + things=1,,3 + + would be returned as: + + ['1', None, '3'] + + while this: + + things=,,, + + would just be retured as: + + [None, None, None, None] Raises HTTPBadRequest: The param was not found in the request, but was required. - """ # 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: items = self._params[name].split(',') - if transform is not None: + + # PERF(kgriffs): Use if-else rather than a DRY approach + # that sets transform to a passthrough function; avoids + # function calling overhead. + if transform is None: + items = [i if i != '' else None + for i in items] + else: try: - items = [transform(x) for x in items] + items = [transform(i) if i != '' else None + for i in items] except ValueError: desc = ('The value of the "' + name + '" query parameter ' 'is not formatted correctly.') diff --git a/falcon/tests/test_query_params.py b/falcon/tests/test_query_params.py index 9fedc0e..5693701 100644 --- a/falcon/tests/test_query_params.py +++ b/falcon/tests/test_query_params.py @@ -181,7 +181,9 @@ class TestQueryParams(testing.TestBase): self.assertEquals(store['echo'], True) def test_list_type(self): - query_string = 'colors=red,green,blue&limit=1' + query_string = ('colors=red,green,blue&limit=1' + '&list-ish1=f,,x&list-ish2=,0&list-ish3=a,,,b' + '&empty1=&empty2=,&empty3=,,') self.simulate_request('/', query_string=query_string) req = self.resource.req @@ -191,12 +193,27 @@ class TestQueryParams(testing.TestBase): self.assertEquals(req.get_param_as_list('limit'), ['1']) self.assertIs(req.get_param_as_list('marker'), None) + self.assertEquals(req.get_param_as_list('empty1'), None) + self.assertEquals(req.get_param_as_list('empty2'), [None, None]) + self.assertEquals(req.get_param_as_list('empty3'), [None, None, None]) + + self.assertEquals(req.get_param_as_list('list-ish1'), + ['f', None, 'x']) + + # Ensure that '0' doesn't get translated to None + self.assertEquals(req.get_param_as_list('list-ish2'), + [None, '0']) + + # Ensure that '0' doesn't get translated to None + self.assertEquals(req.get_param_as_list('list-ish3'), + ['a', None, None, 'b']) + 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' + query_string = 'coord=1.4,13,15.1&limit=100&things=4,,1' self.simulate_request('/', query_string=query_string) req = self.resource.req @@ -206,15 +223,13 @@ class TestQueryParams(testing.TestBase): actual = req.get_param_as_list('coord', transform=float) self.assertEquals(actual, expected) + expected = ['4', None, '1'] + actual = req.get_param_as_list('things', transform=str) + self.assertEquals(actual, expected) + + expected = [4, None, 1] + actual = req.get_param_as_list('things', transform=int) + self.assertEquals(actual, expected) + self.assertRaises(falcon.HTTPBadRequest, req.get_param_as_list, 'coord', transform=int) - - def test_bogus_input(self): - query_string = 'colors=red,green,&limit=1&pickle' - self.simulate_request('/', query_string=query_string) - - req = self.resource.req - self.assertEquals(req.get_param_as_list('colors'), - ['red', 'green', '']) - self.assertEquals(req.get_param('limit'), '1') - self.assertIs(req.get_param('pickle'), None) diff --git a/tox.ini b/tox.ini index 8075b6e..dc24b30 100644 --- a/tox.ini +++ b/tox.ini @@ -24,5 +24,5 @@ commands = py3kwarn falcon deps = flake8 commands = flake8 \ --max-complexity=12 \ - --exclude=.venv,.tox,dist,doc,./falcon/bench/nuts \ + --exclude=./build,.venv,.tox,dist,doc,./falcon/bench/nuts \ --ignore=F403