Skip to content

Commit

Permalink
Improve performance of generating query strings from mappings that co…
Browse files Browse the repository at this point in the history
…ntain sequences
  • Loading branch information
bdraco committed Oct 11, 2024
1 parent b36e63a commit fdf2a6f
Showing 1 changed file with 25 additions and 14 deletions.
39 changes: 25 additions & 14 deletions yarl/_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
from typing import (
TYPE_CHECKING,
Any,
Callable,
Iterable,
Iterator,
List,
SupportsInt,
Tuple,
Expand Down Expand Up @@ -1251,15 +1249,23 @@ def with_path(self, path: str, *, encoded: bool = False) -> "URL":
return URL(self._val._replace(path=path, query="", fragment=""), encoded=True)

@classmethod
def _query_seq_pairs(
cls, quoter: Callable[[str], str], pairs: Iterable[Tuple[str, QueryVariable]]
) -> Iterator[str]:
for key, val in pairs:
if isinstance(val, (list, tuple)):
for v in val:
yield quoter(key) + "=" + quoter(cls._query_var(v))
else:
yield quoter(key) + "=" + quoter(cls._query_var(val))
def _get_str_query_from_sequence_iterable(
self,
items: Iterable[Tuple[Union[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 = self._QUERY_PART_QUOTER
pairs = [
f"{quoter(k)}={quoter(self._query_var(v))}"
for k, val in items
for v in (val if isinstance(val, (list, tuple)) else (val,))
]
return "&".join(pairs)

@staticmethod
def _query_var(v: QueryVariable) -> str:
Expand Down Expand Up @@ -1287,7 +1293,13 @@ def _query_var(v: QueryVariable) -> str:
def _get_str_query_from_iterable(
self, items: Iterable[Tuple[Union[str, istr], str]]
) -> str:
"""Return a query string from an iterable."""
"""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 = self._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.
Expand All @@ -1309,8 +1321,7 @@ def _get_str_query(self, *args: Any, **kwargs: Any) -> Union[str, None]:
if query is None:
return None
if isinstance(query, Mapping):
quoter = self._QUERY_PART_QUOTER
return "&".join(self._query_seq_pairs(quoter, query.items()))
return self._get_str_query_from_sequence_iterable(query.items())
if isinstance(query, str):
return self._QUERY_QUOTER(query)
if isinstance(query, (bytes, bytearray, memoryview)):
Expand Down

0 comments on commit fdf2a6f

Please sign in to comment.