Ensure query params behavior matches standard mappings. (#338)

* QueryParams behavior as standard mapping
This commit is contained in:
Tom Christie 2019-01-22 17:29:39 +00:00 committed by GitHub
parent 0c3a1e4a60
commit 774cb40a17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 27 additions and 15 deletions

View File

@ -231,7 +231,7 @@ class QueryParams(typing.Mapping[str, str]):
def __init__(
self,
params: typing.Mapping[str, str] = None,
params: typing.Union["QueryParams", typing.Mapping[str, str]] = None,
items: typing.List[typing.Tuple[str, str]] = None,
query_string: str = None,
scope: Scope = None,
@ -241,6 +241,9 @@ class QueryParams(typing.Mapping[str, str]):
assert items is None, "Cannot set both 'params' and 'items'"
assert query_string is None, "Cannot set both 'params' and 'query_string'"
assert scope is None, "Cannot set both 'params' and 'scope'"
if isinstance(params, QueryParams):
_items = list(params.multi_items())
else:
_items = list(params.items())
elif items is not None:
assert query_string is None, "Cannot set both 'items' and 'query_string'"
@ -252,25 +255,27 @@ class QueryParams(typing.Mapping[str, str]):
elif scope is not None:
_items = parse_qsl(scope["query_string"].decode("latin-1"))
self._dict = {k: v for k, v in reversed(_items)}
self._dict = {k: v for k, v in _items}
self._list = _items
def getlist(self, key: typing.Any) -> typing.List[str]:
return [item_value for item_key, item_value in self._list if item_key == key]
def keys(self) -> typing.List[str]: # type: ignore
return [key for key, value in self._list]
return list(self._dict.keys())
def values(self) -> typing.List[str]: # type: ignore
return [value for key, value in self._list]
return list(self._dict.values())
def items(self) -> typing.List[typing.Tuple[str, str]]: # type: ignore
return list(self._dict.items())
def multi_items(self) -> typing.List[typing.Tuple[str, str]]:
return list(self._list)
def get(self, key: typing.Any, default: typing.Any = None) -> typing.Any:
if key in self._dict:
return self._dict[key]
else:
return default
def __getitem__(self, key: typing.Any) -> str:
@ -283,7 +288,7 @@ class QueryParams(typing.Mapping[str, str]):
return iter(self.keys())
def __len__(self) -> int:
return len(self._list)
return len(self._dict)
def __eq__(self, other: typing.Any) -> bool:
if not isinstance(other, QueryParams):

View File

@ -172,15 +172,16 @@ def test_queryparams():
assert "a" in q
assert "A" not in q
assert "c" not in q
assert q["a"] == "123"
assert q.get("a") == "123"
assert q["a"] == "456"
assert q.get("a") == "456"
assert q.get("nope", default=None) is None
assert q.getlist("a") == ["123", "456"]
assert q.keys() == ["a", "a", "b"]
assert q.values() == ["123", "456", "789"]
assert q.items() == [("a", "123"), ("a", "456"), ("b", "789")]
assert list(q) == ["a", "a", "b"]
assert dict(q) == {"a": "123", "b": "789"}
assert q.keys() == ["a", "b"]
assert q.values() == ["456", "789"]
assert q.items() == [("a", "456"), ("b", "789")]
assert len(q) == 2
assert list(q) == ["a", "b"]
assert dict(q) == {"a": "456", "b": "789"}
assert str(q) == "a=123&a=456&b=789"
assert repr(q) == "QueryParams(query_string='a=123&a=456&b=789')"
assert QueryParams({"a": "123", "b": "456"}) == QueryParams(
@ -193,4 +194,10 @@ def test_queryparams():
{"b": "456", "a": "123"}
)
assert QueryParams() == QueryParams({})
assert QueryParams(items=[("a", "123"), ("a", "456")]) == QueryParams(
query_string="a=123&a=456"
)
assert QueryParams({"a": "123", "b": "456"}) != "invalid"
q = QueryParams(items=[("a", "123"), ("a", "456")])
assert QueryParams(q) == q