From d78b8bff9ae35674dfff527b367d223e4e91ad25 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 30 Jul 2019 10:17:29 +0100 Subject: [PATCH 1/3] Tweak query param types --- httpx/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httpx/models.py b/httpx/models.py index 9cbdc250e2..d5b3969465 100644 --- a/httpx/models.py +++ b/httpx/models.py @@ -40,7 +40,7 @@ QueryParamTypes = typing.Union[ "QueryParams", typing.Mapping[str, str], - typing.List[typing.Tuple[typing.Any, typing.Any]], + typing.List[typing.Tuple[str, str]], str, ] From 34d5101272e74b90ef79a9a16ca42dd3f7cab9fd Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 30 Jul 2019 10:36:13 +0100 Subject: [PATCH 2/3] Handle primitive data types for queryparams --- httpx/models.py | 11 +++++++---- httpx/utils.py | 15 +++++++++++++++ tests/models/test_queryparams.py | 20 ++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/httpx/models.py b/httpx/models.py index d5b3969465..88a1d724e8 100644 --- a/httpx/models.py +++ b/httpx/models.py @@ -33,14 +33,17 @@ is_known_encoding, normalize_header_key, normalize_header_value, + str_query_param ) +PrimitiveData = typing.Union[str, int, float, bool, type(None)] + URLTypes = typing.Union["URL", str] QueryParamTypes = typing.Union[ "QueryParams", - typing.Mapping[str, str], - typing.List[typing.Tuple[str, str]], + typing.Mapping[PrimitiveData, PrimitiveData], + typing.List[typing.Tuple[PrimitiveData, PrimitiveData]], str, ] @@ -259,8 +262,8 @@ def __init__(self, *args: QueryParamTypes, **kwargs: typing.Any) -> None: else: items = value.items() # type: ignore - self._list = [(str(k), str(v)) for k, v in items] - self._dict = {str(k): str(v) for k, v in items} + self._list = [(str_query_param(k), str_query_param(v)) for k, v in items] + self._dict = {str_query_param(k): str_query_param(v) for k, v in 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] diff --git a/httpx/utils.py b/httpx/utils.py index 3d0d6607d6..e96335f547 100644 --- a/httpx/utils.py +++ b/httpx/utils.py @@ -20,6 +20,21 @@ def normalize_header_value(value: typing.AnyStr, encoding: str = None) -> bytes: return value.encode(encoding or "ascii") +def str_query_param(value: typing.Union[str, int, float, bool, type(None)]) -> str: + """ + Coerce a primitive data type into a string value for query params. + + Note that we prefer JSON-style 'true'/'false' for boolean values here. + """ + if value is True: + return "true" + elif value is False: + return "false" + elif value is None: + return "" + return str(value) + + def is_known_encoding(encoding: str) -> bool: """ Return `True` if `encoding` is a known codec. diff --git a/tests/models/test_queryparams.py b/tests/models/test_queryparams.py index 983b09fb81..fbb559fb62 100644 --- a/tests/models/test_queryparams.py +++ b/tests/models/test_queryparams.py @@ -31,3 +31,23 @@ def test_queryparams(): q = QueryParams([("a", "123"), ("a", "456")]) assert QueryParams(q) == q + + +def test_queryparam_types(): + q = QueryParams({"a": True}) + assert str(q) == "a=true" + + q = QueryParams({"a": False}) + assert str(q) == "a=false" + + q = QueryParams({"a": ""}) + assert str(q) == "a=" + + q = QueryParams({"a": None}) + assert str(q) == "a=" + + q = QueryParams({"a": 1.23}) + assert str(q) == "a=1.23" + + q = QueryParams({"a": 123}) + assert str(q) == "a=123" From 5b5bfa5acda6601a6f4f2c937f82764b796c3fb7 Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Tue, 30 Jul 2019 10:40:08 +0100 Subject: [PATCH 3/3] QueryParam keys as str-only, not PrimitiveData --- httpx/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/httpx/models.py b/httpx/models.py index 88a1d724e8..abed9dc306 100644 --- a/httpx/models.py +++ b/httpx/models.py @@ -42,8 +42,8 @@ QueryParamTypes = typing.Union[ "QueryParams", - typing.Mapping[PrimitiveData, PrimitiveData], - typing.List[typing.Tuple[PrimitiveData, PrimitiveData]], + typing.Mapping[str, PrimitiveData], + typing.List[typing.Tuple[str, PrimitiveData]], str, ] @@ -262,8 +262,8 @@ def __init__(self, *args: QueryParamTypes, **kwargs: typing.Any) -> None: else: items = value.items() # type: ignore - self._list = [(str_query_param(k), str_query_param(v)) for k, v in items] - self._dict = {str_query_param(k): str_query_param(v) for k, v in items} + self._list = [(str(k), str_query_param(v)) for k, v in items] + self._dict = {str(k): str_query_param(v) for k, v in 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]