Edit on GitHub

mitmproxy.coretypes.multidict

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

A MultiDict is a dictionary-like data structure that supports multiple values per key.

fields: tuple[tuple[~KT, ~VT], ...]

The underlying raw datastructure.

def get_all(self, key: ~KT) -> list[~VT]:
78    def get_all(self, key: KT) -> list[VT]:
79        """
80        Return the list of all values for a given key.
81        If that key is not in the MultiDict, the return value will be an empty list.
82        """
83        key = self._kconv(key)
84        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.

def set_all(self, key: ~KT, values: list[~VT]) -> None:
 86    def set_all(self, key: KT, values: list[VT]) -> None:
 87        """
 88        Remove the old values for a key and add new ones.
 89        """
 90        key_kconv = self._kconv(key)
 91
 92        new_fields: list[tuple[KT, VT]] = []
 93        for field in self.fields:
 94            if self._kconv(field[0]) == key_kconv:
 95                if values:
 96                    new_fields.append((field[0], values.pop(0)))
 97            else:
 98                new_fields.append(field)
 99        while values:
100            new_fields.append((key, values.pop(0)))
101        self.fields = tuple(new_fields)

Remove the old values for a key and add new ones.

def add(self, key: ~KT, value: ~VT) -> None:
103    def add(self, key: KT, value: VT) -> None:
104        """
105        Add an additional value for the given key at the bottom.
106        """
107        self.insert(len(self.fields), key, value)

Add an additional value for the given key at the bottom.

def insert(self, index: int, key: ~KT, value: ~VT) -> None:
109    def insert(self, index: int, key: KT, value: VT) -> None:
110        """
111        Insert an additional value for the given key at the specified position.
112        """
113        item = (key, value)
114        self.fields = self.fields[:index] + (item,) + self.fields[index:]

Insert an additional value for the given key at the specified position.

def keys(self, multi: bool = False):
116    def keys(self, multi: bool = False):
117        """
118        Get all keys.
119
120        If `multi` is True, one key per value will be returned.
121        If `multi` is False, duplicate keys will only be returned once.
122        """
123        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.

def values(self, multi: bool = False):
125    def values(self, multi: bool = False):
126        """
127        Get all values.
128
129        If `multi` is True, all values will be returned.
130        If `multi` is False, only the first value per key will be returned.
131        """
132        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.

def items(self, multi: bool = False):
134    def items(self, multi: bool = False):
135        """
136        Get all (key, value) tuples.
137
138        If `multi` is True, all `(key, value)` pairs will be returned.
139        If False, only one tuple per key is returned.
140        """
141        if multi:
142            return self.fields
143        else:
144            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.

Inherited Members
collections.abc.MutableMapping
pop
popitem
clear
update
setdefault
collections.abc.Mapping
get
class MultiDict(mitmproxy.coretypes.multidict._MultiDict[~KT, ~VT], mitmproxy.coretypes.serializable.Serializable):
147class MultiDict(_MultiDict[KT, VT], serializable.Serializable):
148    """A concrete MultiDict, storing its own data."""
149
150    def __init__(self, fields=()):
151        super().__init__()
152        self.fields = tuple(tuple(i) for i in fields)
153
154    @staticmethod
155    def _reduce_values(values):
156        return values[0]
157
158    @staticmethod
159    def _kconv(key):
160        return key
161
162    def get_state(self):
163        return self.fields
164
165    def set_state(self, state):
166        self.fields = tuple(tuple(x) for x in state)
167
168    @classmethod
169    def from_state(cls, state):
170        return cls(state)

A concrete MultiDict, storing its own data.

MultiDict(fields=())
150    def __init__(self, fields=()):
151        super().__init__()
152        self.fields = tuple(tuple(i) for i in fields)
Inherited Members
mitmproxy.coretypes.serializable.Serializable
copy
collections.abc.MutableMapping
pop
popitem
clear
update
setdefault
collections.abc.Mapping
get
class MultiDictView(mitmproxy.coretypes.multidict._MultiDict[~KT, ~VT]):
173class MultiDictView(_MultiDict[KT, VT]):
174    """
175    The MultiDictView provides the MultiDict interface over calculated data.
176    The view itself contains no state - data is retrieved from the parent on
177    request, and stored back to the parent on change.
178    """
179
180    def __init__(self, getter, setter):
181        self._getter = getter
182        self._setter = setter
183        super().__init__()
184
185    @staticmethod
186    def _kconv(key):
187        # All request-attributes are case-sensitive.
188        return key
189
190    @staticmethod
191    def _reduce_values(values):
192        # We just return the first element if
193        # multiple elements exist with the same key.
194        return values[0]
195
196    @property  # type: ignore
197    def fields(self):
198        return self._getter()
199
200    @fields.setter
201    def fields(self, value):
202        self._setter(value)
203
204    def copy(self) -> "MultiDict[KT,VT]":
205        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.

MultiDictView(getter, setter)
180    def __init__(self, getter, setter):
181        self._getter = getter
182        self._setter = setter
183        super().__init__()
fields

The underlying raw datastructure.

def copy(self) -> mitmproxy.coretypes.multidict.MultiDict[~KT, ~VT]:
204    def copy(self) -> "MultiDict[KT,VT]":
205        return MultiDict(self.fields)
Inherited Members
collections.abc.MutableMapping
pop
popitem
clear
update
setdefault
collections.abc.Mapping
get