mitmproxy.coretypes.multidict
1from abc import ABCMeta 2from abc import abstractmethod 3from collections.abc import Iterator 4from collections.abc import MutableMapping 5from collections.abc import Sequence 6from typing import TypeVar 7 8from mitmproxy.coretypes import serializable 9 10KT = TypeVar("KT") 11VT = TypeVar("VT") 12 13 14class _MultiDict(MutableMapping[KT, VT], metaclass=ABCMeta): 15 """ 16 A MultiDict is a dictionary-like data structure that supports multiple values per key. 17 """ 18 19 fields: tuple[tuple[KT, VT], ...] 20 """The underlying raw datastructure.""" 21 22 def __repr__(self): 23 fields = (repr(field) for field in self.fields) 24 return "{cls}[{fields}]".format( 25 cls=type(self).__name__, fields=", ".join(fields) 26 ) 27 28 @staticmethod 29 @abstractmethod 30 def _reduce_values(values: Sequence[VT]) -> VT: 31 """ 32 If a user accesses multidict["foo"], this method 33 reduces all values for "foo" to a single value that is returned. 34 For example, HTTP headers are folded, whereas we will just take 35 the first cookie we found with that name. 36 """ 37 38 @staticmethod 39 @abstractmethod 40 def _kconv(key: KT) -> KT: 41 """ 42 This method converts a key to its canonical representation. 43 For example, HTTP headers are case-insensitive, so this method returns key.lower(). 44 """ 45 46 def __getitem__(self, key: KT) -> VT: 47 values = self.get_all(key) 48 if not values: 49 raise KeyError(key) 50 return self._reduce_values(values) 51 52 def __setitem__(self, key: KT, value: VT) -> None: 53 self.set_all(key, [value]) 54 55 def __delitem__(self, key: KT) -> None: 56 if key not in self: 57 raise KeyError(key) 58 key = self._kconv(key) 59 self.fields = tuple( 60 field for field in self.fields if key != self._kconv(field[0]) 61 ) 62 63 def __iter__(self) -> Iterator[KT]: 64 seen = set() 65 for key, _ in self.fields: 66 key_kconv = self._kconv(key) 67 if key_kconv not in seen: 68 seen.add(key_kconv) 69 yield key 70 71 def __len__(self) -> int: 72 return len({self._kconv(key) for key, _ in self.fields}) 73 74 def __eq__(self, other) -> bool: 75 if isinstance(other, MultiDict): 76 return self.fields == other.fields 77 return False 78 79 def get_all(self, key: KT) -> list[VT]: 80 """ 81 Return the list of all values for a given key. 82 If that key is not in the MultiDict, the return value will be an empty list. 83 """ 84 key = self._kconv(key) 85 return [value for k, value in self.fields if self._kconv(k) == key] 86 87 def set_all(self, key: KT, values: list[VT]) -> None: 88 """ 89 Remove the old values for a key and add new ones. 90 """ 91 key_kconv = self._kconv(key) 92 values = values.copy() 93 94 new_fields: list[tuple[KT, VT]] = [] 95 for field in self.fields: 96 if self._kconv(field[0]) == key_kconv: 97 if values: 98 new_fields.append((field[0], values.pop(0))) 99 else: 100 new_fields.append(field) 101 while values: 102 new_fields.append((key, values.pop(0))) 103 self.fields = tuple(new_fields) 104 105 def add(self, key: KT, value: VT) -> None: 106 """ 107 Add an additional value for the given key at the bottom. 108 """ 109 self.insert(len(self.fields), key, value) 110 111 def insert(self, index: int, key: KT, value: VT) -> None: 112 """ 113 Insert an additional value for the given key at the specified position. 114 """ 115 item = (key, value) 116 self.fields = self.fields[:index] + (item,) + self.fields[index:] 117 118 def keys(self, multi: bool = False): 119 """ 120 Get all keys. 121 122 If `multi` is True, one key per value will be returned. 123 If `multi` is False, duplicate keys will only be returned once. 124 """ 125 return (k for k, _ in self.items(multi)) 126 127 def values(self, multi: bool = False): 128 """ 129 Get all values. 130 131 If `multi` is True, all values will be returned. 132 If `multi` is False, only the first value per key will be returned. 133 """ 134 return (v for _, v in self.items(multi)) 135 136 def items(self, multi: bool = False): 137 """ 138 Get all (key, value) tuples. 139 140 If `multi` is True, all `(key, value)` pairs will be returned. 141 If False, only one tuple per key is returned. 142 """ 143 if multi: 144 return self.fields 145 else: 146 return super().items() 147 148 149class MultiDict(_MultiDict[KT, VT], serializable.Serializable): 150 """A concrete MultiDict, storing its own data.""" 151 152 def __init__(self, fields=()): 153 super().__init__() 154 self.fields = tuple(tuple(i) for i in fields) # type: ignore 155 156 @staticmethod 157 def _reduce_values(values): 158 return values[0] 159 160 @staticmethod 161 def _kconv(key): 162 return key 163 164 def get_state(self): 165 return self.fields 166 167 def set_state(self, state): 168 self.fields = tuple(tuple(x) for x in state) # type: ignore 169 170 @classmethod 171 def from_state(cls, state): 172 return cls(state) 173 174 175class MultiDictView(_MultiDict[KT, VT]): 176 """ 177 The MultiDictView provides the MultiDict interface over calculated data. 178 The view itself contains no state - data is retrieved from the parent on 179 request, and stored back to the parent on change. 180 """ 181 182 def __init__(self, getter, setter): 183 self._getter = getter 184 self._setter = setter 185 super().__init__() 186 187 @staticmethod 188 def _kconv(key): 189 # All request-attributes are case-sensitive. 190 return key 191 192 @staticmethod 193 def _reduce_values(values): 194 # We just return the first element if 195 # multiple elements exist with the same key. 196 return values[0] 197 198 @property # type: ignore 199 def fields(self): 200 return self._getter() 201 202 @fields.setter 203 def fields(self, value): 204 self._setter(value) 205 206 def copy(self) -> "MultiDict[KT,VT]": 207 return MultiDict(self.fields)
15class _MultiDict(MutableMapping[KT, VT], metaclass=ABCMeta): 16 """ 17 A MultiDict is a dictionary-like data structure that supports multiple values per key. 18 """ 19 20 fields: tuple[tuple[KT, VT], ...] 21 """The underlying raw datastructure.""" 22 23 def __repr__(self): 24 fields = (repr(field) for field in self.fields) 25 return "{cls}[{fields}]".format( 26 cls=type(self).__name__, fields=", ".join(fields) 27 ) 28 29 @staticmethod 30 @abstractmethod 31 def _reduce_values(values: Sequence[VT]) -> VT: 32 """ 33 If a user accesses multidict["foo"], this method 34 reduces all values for "foo" to a single value that is returned. 35 For example, HTTP headers are folded, whereas we will just take 36 the first cookie we found with that name. 37 """ 38 39 @staticmethod 40 @abstractmethod 41 def _kconv(key: KT) -> KT: 42 """ 43 This method converts a key to its canonical representation. 44 For example, HTTP headers are case-insensitive, so this method returns key.lower(). 45 """ 46 47 def __getitem__(self, key: KT) -> VT: 48 values = self.get_all(key) 49 if not values: 50 raise KeyError(key) 51 return self._reduce_values(values) 52 53 def __setitem__(self, key: KT, value: VT) -> None: 54 self.set_all(key, [value]) 55 56 def __delitem__(self, key: KT) -> None: 57 if key not in self: 58 raise KeyError(key) 59 key = self._kconv(key) 60 self.fields = tuple( 61 field for field in self.fields if key != self._kconv(field[0]) 62 ) 63 64 def __iter__(self) -> Iterator[KT]: 65 seen = set() 66 for key, _ in self.fields: 67 key_kconv = self._kconv(key) 68 if key_kconv not in seen: 69 seen.add(key_kconv) 70 yield key 71 72 def __len__(self) -> int: 73 return len({self._kconv(key) for key, _ in self.fields}) 74 75 def __eq__(self, other) -> bool: 76 if isinstance(other, MultiDict): 77 return self.fields == other.fields 78 return False 79 80 def get_all(self, key: KT) -> list[VT]: 81 """ 82 Return the list of all values for a given key. 83 If that key is not in the MultiDict, the return value will be an empty list. 84 """ 85 key = self._kconv(key) 86 return [value for k, value in self.fields if self._kconv(k) == key] 87 88 def set_all(self, key: KT, values: list[VT]) -> None: 89 """ 90 Remove the old values for a key and add new ones. 91 """ 92 key_kconv = self._kconv(key) 93 values = values.copy() 94 95 new_fields: list[tuple[KT, VT]] = [] 96 for field in self.fields: 97 if self._kconv(field[0]) == key_kconv: 98 if values: 99 new_fields.append((field[0], values.pop(0))) 100 else: 101 new_fields.append(field) 102 while values: 103 new_fields.append((key, values.pop(0))) 104 self.fields = tuple(new_fields) 105 106 def add(self, key: KT, value: VT) -> None: 107 """ 108 Add an additional value for the given key at the bottom. 109 """ 110 self.insert(len(self.fields), key, value) 111 112 def insert(self, index: int, key: KT, value: VT) -> None: 113 """ 114 Insert an additional value for the given key at the specified position. 115 """ 116 item = (key, value) 117 self.fields = self.fields[:index] + (item,) + self.fields[index:] 118 119 def keys(self, multi: bool = False): 120 """ 121 Get all keys. 122 123 If `multi` is True, one key per value will be returned. 124 If `multi` is False, duplicate keys will only be returned once. 125 """ 126 return (k for k, _ in self.items(multi)) 127 128 def values(self, multi: bool = False): 129 """ 130 Get all values. 131 132 If `multi` is True, all values will be returned. 133 If `multi` is False, only the first value per key will be returned. 134 """ 135 return (v for _, v in self.items(multi)) 136 137 def items(self, multi: bool = False): 138 """ 139 Get all (key, value) tuples. 140 141 If `multi` is True, all `(key, value)` pairs will be returned. 142 If False, only one tuple per key is returned. 143 """ 144 if multi: 145 return self.fields 146 else: 147 return super().items()
A MultiDict is a dictionary-like data structure that supports multiple values per key.
80 def get_all(self, key: KT) -> list[VT]: 81 """ 82 Return the list of all values for a given key. 83 If that key is not in the MultiDict, the return value will be an empty list. 84 """ 85 key = self._kconv(key) 86 return [value for k, value in self.fields if self._kconv(k) == key]
Return the list of all values for a given key. If that key is not in the MultiDict, the return value will be an empty list.
88 def set_all(self, key: KT, values: list[VT]) -> None: 89 """ 90 Remove the old values for a key and add new ones. 91 """ 92 key_kconv = self._kconv(key) 93 values = values.copy() 94 95 new_fields: list[tuple[KT, VT]] = [] 96 for field in self.fields: 97 if self._kconv(field[0]) == key_kconv: 98 if values: 99 new_fields.append((field[0], values.pop(0))) 100 else: 101 new_fields.append(field) 102 while values: 103 new_fields.append((key, values.pop(0))) 104 self.fields = tuple(new_fields)
Remove the old values for a key and add new ones.
106 def add(self, key: KT, value: VT) -> None: 107 """ 108 Add an additional value for the given key at the bottom. 109 """ 110 self.insert(len(self.fields), key, value)
Add an additional value for the given key at the bottom.
112 def insert(self, index: int, key: KT, value: VT) -> None: 113 """ 114 Insert an additional value for the given key at the specified position. 115 """ 116 item = (key, value) 117 self.fields = self.fields[:index] + (item,) + self.fields[index:]
Insert an additional value for the given key at the specified position.
119 def keys(self, multi: bool = False): 120 """ 121 Get all keys. 122 123 If `multi` is True, one key per value will be returned. 124 If `multi` is False, duplicate keys will only be returned once. 125 """ 126 return (k for k, _ in self.items(multi))
Get all keys.
If multi is True, one key per value will be returned.
If multi is False, duplicate keys will only be returned once.
128 def values(self, multi: bool = False): 129 """ 130 Get all values. 131 132 If `multi` is True, all values will be returned. 133 If `multi` is False, only the first value per key will be returned. 134 """ 135 return (v for _, v in self.items(multi))
Get all values.
If multi is True, all values will be returned.
If multi is False, only the first value per key will be returned.
137 def items(self, multi: bool = False): 138 """ 139 Get all (key, value) tuples. 140 141 If `multi` is True, all `(key, value)` pairs will be returned. 142 If False, only one tuple per key is returned. 143 """ 144 if multi: 145 return self.fields 146 else: 147 return super().items()
Get all (key, value) tuples.
If multi is True, all (key, value) pairs will be returned.
If False, only one tuple per key is returned.
150class MultiDict(_MultiDict[KT, VT], serializable.Serializable): 151 """A concrete MultiDict, storing its own data.""" 152 153 def __init__(self, fields=()): 154 super().__init__() 155 self.fields = tuple(tuple(i) for i in fields) # type: ignore 156 157 @staticmethod 158 def _reduce_values(values): 159 return values[0] 160 161 @staticmethod 162 def _kconv(key): 163 return key 164 165 def get_state(self): 166 return self.fields 167 168 def set_state(self, state): 169 self.fields = tuple(tuple(x) for x in state) # type: ignore 170 171 @classmethod 172 def from_state(cls, state): 173 return cls(state)
A concrete MultiDict, storing its own data.
176class MultiDictView(_MultiDict[KT, VT]): 177 """ 178 The MultiDictView provides the MultiDict interface over calculated data. 179 The view itself contains no state - data is retrieved from the parent on 180 request, and stored back to the parent on change. 181 """ 182 183 def __init__(self, getter, setter): 184 self._getter = getter 185 self._setter = setter 186 super().__init__() 187 188 @staticmethod 189 def _kconv(key): 190 # All request-attributes are case-sensitive. 191 return key 192 193 @staticmethod 194 def _reduce_values(values): 195 # We just return the first element if 196 # multiple elements exist with the same key. 197 return values[0] 198 199 @property # type: ignore 200 def fields(self): 201 return self._getter() 202 203 @fields.setter 204 def fields(self, value): 205 self._setter(value) 206 207 def copy(self) -> "MultiDict[KT,VT]": 208 return MultiDict(self.fields)
The MultiDictView provides the MultiDict interface over calculated data. The view itself contains no state - data is retrieved from the parent on request, and stored back to the parent on change.