Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/yarl/_query.py: 22%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Query string handling."""
3import math
4from collections.abc import Iterable, Mapping, Sequence
5from typing import TYPE_CHECKING, Any, SupportsInt, cast
7from multidict import istr
9from ._quoters import QUERY_PART_QUOTER, QUERY_QUOTER
11SimpleQuery = str | SupportsInt | float
12QueryVariable = SimpleQuery | Sequence[SimpleQuery]
13Query = str | Mapping[str, QueryVariable] | Sequence[tuple[str, QueryVariable]] | None
16def query_var(v: SimpleQuery) -> str:
17 """Convert a query variable to a string.
19 Note: Objects implementing the ``__int__`` data model method (typed as
20 ``SupportsInt``; e.g. ``uuid.UUID``) are converted via ``int()`` first.
21 Callers should convert such values to ``str`` explicitly if the string
22 representation is desired.
23 """
24 cls = type(v)
25 if cls is int: # Fast path for non-subclassed int
26 return str(v)
27 if isinstance(v, str):
28 return v
29 if isinstance(v, float):
30 if math.isinf(v):
31 raise ValueError("float('inf') is not supported")
32 if math.isnan(v):
33 raise ValueError("float('nan') is not supported")
34 return str(float(v))
35 if cls is not bool and isinstance(v, SupportsInt):
36 return str(int(v))
37 raise TypeError(
38 "Invalid variable type: value "
39 "should be str, int or float, got {!r} "
40 "of type {}".format(v, cls)
41 )
44def get_str_query_from_sequence_iterable(
45 items: Iterable[tuple[str | istr, QueryVariable]],
46) -> str:
47 """Return a query string from a sequence of (key, value) pairs.
49 value is a single value or a sequence of values for the key
51 The sequence of values must be a list or tuple.
52 """
53 quoter = QUERY_PART_QUOTER
54 pairs = [
55 f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}"
56 for k, val in items
57 for v in (
58 val if type(val) is not str and isinstance(val, (list, tuple)) else (val,)
59 )
60 ]
61 return "&".join(pairs)
64def get_str_query_from_iterable(
65 items: Iterable[tuple[str | istr, SimpleQuery]],
66) -> str:
67 """Return a query string from an iterable.
69 The iterable must contain (key, value) pairs.
71 The values are not allowed to be sequences, only single values are
72 allowed. For sequences, use `_get_str_query_from_sequence_iterable`.
73 """
74 quoter = QUERY_PART_QUOTER
75 # A listcomp is used since listcomps are inlined on CPython 3.12+ and
76 # they are a bit faster than a generator expression.
77 pairs = [
78 f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" for k, v in items
79 ]
80 return "&".join(pairs)
83def get_str_query(*args: Any, **kwargs: Any) -> str | None:
84 """Return a query string from supported args."""
85 query: (
86 str
87 | Mapping[str, QueryVariable]
88 | Sequence[tuple[str | istr, SimpleQuery]]
89 | None
90 )
91 if kwargs:
92 if args:
93 msg = "Either kwargs or single query parameter must be present"
94 raise ValueError(msg)
95 query = kwargs
96 elif len(args) == 1:
97 query = args[0]
98 else:
99 raise ValueError("Either kwargs or single query parameter must be present")
101 if query is None:
102 return None
103 if not query:
104 return ""
105 if type(query) is dict:
106 return get_str_query_from_sequence_iterable(query.items())
107 if type(query) is str or isinstance(query, str):
108 return QUERY_QUOTER(query)
109 if isinstance(query, Mapping):
110 return get_str_query_from_sequence_iterable(query.items())
111 if isinstance(query, (bytes, bytearray, memoryview)):
112 msg = "Invalid query type: bytes, bytearray and memoryview are forbidden"
113 raise TypeError(msg)
114 if isinstance(query, Sequence):
115 # We don't expect sequence values if we're given a list of pairs
116 # already; only mappings like builtin `dict` which can't have the
117 # same key pointing to multiple values are allowed to use
118 # `_query_seq_pairs`.
119 if TYPE_CHECKING:
120 query = cast(Sequence[tuple[str | istr, SimpleQuery]], query)
121 return get_str_query_from_iterable(query)
122 raise TypeError(
123 "Invalid query type: only str, mapping or "
124 "sequence of (key, value) pairs is allowed"
125 )