-
-
Notifications
You must be signed in to change notification settings - Fork 188
Expand file tree
/
Copy path_query.py
More file actions
126 lines (108 loc) · 4.16 KB
/
_query.py
File metadata and controls
126 lines (108 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""Query string handling."""
import math
from collections.abc import Iterable, Mapping, Sequence
from typing import TYPE_CHECKING, Any, SupportsInt, Union, cast
from multidict import istr
from ._quoters import QUERY_PART_QUOTER, QUERY_QUOTER
SimpleQuery = Union[str, SupportsInt, float]
QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]]
Query = Union[
None, str, Mapping[str, QueryVariable], Sequence[tuple[str, QueryVariable]]
]
def query_var(v: SimpleQuery) -> str:
"""Convert a query variable to a string.
Note: Objects implementing ``SupportsInt`` (e.g. ``uuid.UUID``) will be
converted via ``int()`` first. Callers should convert such values to ``str``
explicitly if the string representation is desired.
"""
cls = type(v)
if cls is int: # Fast path for non-subclassed int
return str(v)
if isinstance(v, str):
return v
if isinstance(v, float):
if math.isinf(v):
raise ValueError("float('inf') is not supported")
if math.isnan(v):
raise ValueError("float('nan') is not supported")
return str(float(v))
if cls is not bool and isinstance(v, SupportsInt):
return str(int(v))
raise TypeError(
"Invalid variable type: value "
"should be str, int or float, got {!r} "
"of type {}".format(v, cls)
)
def get_str_query_from_sequence_iterable(
items: Iterable[tuple[str | istr, QueryVariable]],
) -> str:
"""Return a query string from a sequence of (key, value) pairs.
value is a single value or a sequence of values for the key
The sequence of values must be a list or tuple.
"""
quoter = QUERY_PART_QUOTER
pairs = [
f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}"
for k, val in items
for v in (
val if type(val) is not str and isinstance(val, (list, tuple)) else (val,)
)
]
return "&".join(pairs)
def get_str_query_from_iterable(
items: Iterable[tuple[str | istr, SimpleQuery]],
) -> str:
"""Return a query string from an iterable.
The iterable must contain (key, value) pairs.
The values are not allowed to be sequences, only single values are
allowed. For sequences, use `_get_str_query_from_sequence_iterable`.
"""
quoter = QUERY_PART_QUOTER
# A listcomp is used since listcomps are inlined on CPython 3.12+ and
# they are a bit faster than a generator expression.
pairs = [
f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" for k, v in items
]
return "&".join(pairs)
def get_str_query(*args: Any, **kwargs: Any) -> str | None:
"""Return a query string from supported args."""
query: (
str
| Mapping[str, QueryVariable]
| Sequence[tuple[str | istr, SimpleQuery]]
| None
)
if kwargs:
if args:
msg = "Either kwargs or single query parameter must be present"
raise ValueError(msg)
query = kwargs
elif len(args) == 1:
query = args[0]
else:
raise ValueError("Either kwargs or single query parameter must be present")
if query is None:
return None
if not query:
return ""
if type(query) is dict:
return get_str_query_from_sequence_iterable(query.items())
if type(query) is str or isinstance(query, str):
return QUERY_QUOTER(query)
if isinstance(query, Mapping):
return get_str_query_from_sequence_iterable(query.items())
if isinstance(query, (bytes, bytearray, memoryview)):
msg = "Invalid query type: bytes, bytearray and memoryview are forbidden"
raise TypeError(msg)
if isinstance(query, Sequence):
# We don't expect sequence values if we're given a list of pairs
# already; only mappings like builtin `dict` which can't have the
# same key pointing to multiple values are allowed to use
# `_query_seq_pairs`.
if TYPE_CHECKING:
query = cast(Sequence[tuple[Union[str, istr], SimpleQuery]], query)
return get_str_query_from_iterable(query)
raise TypeError(
"Invalid query type: only str, mapping or "
"sequence of (key, value) pairs is allowed"
)