from .multipart import multipart_encode
from .status_codes import StatusCode
from .utils import (
+ flatten_queryparams,
guess_json_utf,
is_known_encoding,
normalize_header_key,
QueryParamTypes = typing.Union[
"QueryParams",
- typing.Mapping[str, PrimitiveData],
+ typing.Mapping[str, typing.Union[PrimitiveData, typing.Sequence[PrimitiveData]]],
typing.List[typing.Tuple[str, PrimitiveData]],
str,
]
value = args[0] if args else kwargs
+ items: typing.Sequence[typing.Tuple[str, PrimitiveData]]
if isinstance(value, str):
items = parse_qsl(value)
elif isinstance(value, QueryParams):
items = value.multi_items()
elif isinstance(value, list):
- items = value # type: ignore
+ items = value
else:
- items = value.items() # type: ignore
+ items = flatten_queryparams(value)
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}
import codecs
+import collections
import logging
import netrc
import os
from types import TracebackType
from urllib.request import getproxies
+if typing.TYPE_CHECKING: # pragma: no cover
+ from .models import PrimitiveData
+
def normalize_header_key(value: typing.AnyStr, encoding: str = None) -> bytes:
"""
return value.encode(encoding or "ascii")
-def str_query_param(value: typing.Optional[typing.Union[str, int, float, bool]]) -> str:
+def str_query_param(value: "PrimitiveData") -> str:
"""
Coerce a primitive data type into a string value for query params.
return value[1:-1] if value[0] == value[-1] == '"' else value
+def flatten_queryparams(
+ queryparams: typing.Mapping[
+ str, typing.Union["PrimitiveData", typing.Sequence["PrimitiveData"]]
+ ]
+) -> typing.List[typing.Tuple[str, "PrimitiveData"]]:
+ """
+ Convert a mapping of query params into a flat list of two-tuples
+ representing each item.
+
+ Example:
+ >>> flatten_queryparams_values({"q": "httpx", "tag": ["python", "dev"]})
+ [("q", "httpx), ("tag", "python"), ("tag", "dev")]
+ """
+ items = []
+
+ for k, v in queryparams.items():
+ if isinstance(v, collections.abc.Sequence) and not isinstance(v, (str, bytes)):
+ for u in v:
+ items.append((k, u))
+ else:
+ items.append((k, typing.cast("PrimitiveData", v)))
+
+ return items
+
+
class ElapsedTimer:
def __init__(self) -> None:
self.start: float = perf_counter()
+import pytest
+
from httpx import QueryParams
-def test_queryparams():
- q = QueryParams("a=123&a=456&b=789")
+@pytest.mark.parametrize(
+ "source",
+ [
+ "a=123&a=456&b=789",
+ {"a": ["123", "456"], "b": 789},
+ {"a": ("123", "456"), "b": 789},
+ ],
+)
+def test_queryparams(source):
+ q = QueryParams(source)
assert "a" in q
assert "A" not in q
assert "c" not in q