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

59 statements  

1"""Query string handling.""" 

2 

3import math 

4from collections.abc import Iterable, Mapping, Sequence 

5from typing import TYPE_CHECKING, Any, SupportsInt, Union, cast 

6 

7from multidict import istr 

8 

9from ._quoters import QUERY_PART_QUOTER, QUERY_QUOTER 

10 

11SimpleQuery = Union[str, SupportsInt, float] 

12QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]] 

13Query = Union[ 

14 None, str, Mapping[str, QueryVariable], Sequence[tuple[str, QueryVariable]] 

15] 

16 

17 

18def query_var(v: SimpleQuery) -> str: 

19 """Convert a query variable to a string. 

20 

21 Note: Objects implementing the ``__int__`` data model method (typed as 

22 ``SupportsInt``; e.g. ``uuid.UUID``) are converted via ``int()`` first. 

23 Callers should convert such values to ``str`` explicitly if the string 

24 representation is desired. 

25 """ 

26 cls = type(v) 

27 if cls is int: # Fast path for non-subclassed int 

28 return str(v) 

29 if isinstance(v, str): 

30 return v 

31 if isinstance(v, float): 

32 if math.isinf(v): 

33 raise ValueError("float('inf') is not supported") 

34 if math.isnan(v): 

35 raise ValueError("float('nan') is not supported") 

36 return str(float(v)) 

37 if cls is not bool and isinstance(v, SupportsInt): 

38 return str(int(v)) 

39 raise TypeError( 

40 "Invalid variable type: value " 

41 "should be str, int or float, got {!r} " 

42 "of type {}".format(v, cls) 

43 ) 

44 

45 

46def get_str_query_from_sequence_iterable( 

47 items: Iterable[tuple[str | istr, QueryVariable]], 

48) -> str: 

49 """Return a query string from a sequence of (key, value) pairs. 

50 

51 value is a single value or a sequence of values for the key 

52 

53 The sequence of values must be a list or tuple. 

54 """ 

55 quoter = QUERY_PART_QUOTER 

56 pairs = [ 

57 f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" 

58 for k, val in items 

59 for v in ( 

60 val if type(val) is not str and isinstance(val, (list, tuple)) else (val,) 

61 ) 

62 ] 

63 return "&".join(pairs) 

64 

65 

66def get_str_query_from_iterable( 

67 items: Iterable[tuple[str | istr, SimpleQuery]], 

68) -> str: 

69 """Return a query string from an iterable. 

70 

71 The iterable must contain (key, value) pairs. 

72 

73 The values are not allowed to be sequences, only single values are 

74 allowed. For sequences, use `_get_str_query_from_sequence_iterable`. 

75 """ 

76 quoter = QUERY_PART_QUOTER 

77 # A listcomp is used since listcomps are inlined on CPython 3.12+ and 

78 # they are a bit faster than a generator expression. 

79 pairs = [ 

80 f"{quoter(k)}={quoter(v if type(v) is str else query_var(v))}" for k, v in items 

81 ] 

82 return "&".join(pairs) 

83 

84 

85def get_str_query(*args: Any, **kwargs: Any) -> str | None: 

86 """Return a query string from supported args.""" 

87 query: ( 

88 str 

89 | Mapping[str, QueryVariable] 

90 | Sequence[tuple[str | istr, SimpleQuery]] 

91 | None 

92 ) 

93 if kwargs: 

94 if args: 

95 msg = "Either kwargs or single query parameter must be present" 

96 raise ValueError(msg) 

97 query = kwargs 

98 elif len(args) == 1: 

99 query = args[0] 

100 else: 

101 raise ValueError("Either kwargs or single query parameter must be present") 

102 

103 if query is None: 

104 return None 

105 if not query: 

106 return "" 

107 if type(query) is dict: 

108 return get_str_query_from_sequence_iterable(query.items()) 

109 if type(query) is str or isinstance(query, str): 

110 return QUERY_QUOTER(query) 

111 if isinstance(query, Mapping): 

112 return get_str_query_from_sequence_iterable(query.items()) 

113 if isinstance(query, (bytes, bytearray, memoryview)): 

114 msg = "Invalid query type: bytes, bytearray and memoryview are forbidden" 

115 raise TypeError(msg) 

116 if isinstance(query, Sequence): 

117 # We don't expect sequence values if we're given a list of pairs 

118 # already; only mappings like builtin `dict` which can't have the 

119 # same key pointing to multiple values are allowed to use 

120 # `_query_seq_pairs`. 

121 if TYPE_CHECKING: 

122 query = cast(Sequence[tuple[Union[str, istr], SimpleQuery]], query) 

123 return get_str_query_from_iterable(query) 

124 raise TypeError( 

125 "Invalid query type: only str, mapping or " 

126 "sequence of (key, value) pairs is allowed" 

127 )