Efficient data structure for manipulating HTTP headers. A :class:`list` of ``(name, values)`` is inefficient for lookups. A :class:`dict` doesn't suffice because header names are case-insensitive and multiple occurrences of headers with the same name are possible. :class:`Hea
| 25 | |
| 26 | |
| 27 | class Headers(MutableMapping[str, str]): |
| 28 | """ |
| 29 | Efficient data structure for manipulating HTTP headers. |
| 30 | |
| 31 | A :class:`list` of ``(name, values)`` is inefficient for lookups. |
| 32 | |
| 33 | A :class:`dict` doesn't suffice because header names are case-insensitive |
| 34 | and multiple occurrences of headers with the same name are possible. |
| 35 | |
| 36 | :class:`Headers` stores HTTP headers in a hybrid data structure to provide |
| 37 | efficient insertions and lookups while preserving the original data. |
| 38 | |
| 39 | In order to account for multiple values with minimal hassle, |
| 40 | :class:`Headers` follows this logic: |
| 41 | |
| 42 | - When getting a header with ``headers[name]``: |
| 43 | - if there's no value, :exc:`KeyError` is raised; |
| 44 | - if there's exactly one value, it's returned; |
| 45 | - if there's more than one value, :exc:`MultipleValuesError` is raised. |
| 46 | |
| 47 | - When setting a header with ``headers[name] = value``, the value is |
| 48 | appended to the list of values for that header. |
| 49 | |
| 50 | - When deleting a header with ``del headers[name]``, all values for that |
| 51 | header are removed (this is slow). |
| 52 | |
| 53 | Other methods for manipulating headers are consistent with this logic. |
| 54 | |
| 55 | As long as no header occurs multiple times, :class:`Headers` behaves like |
| 56 | :class:`dict`, except keys are lower-cased to provide case-insensitivity. |
| 57 | |
| 58 | Two methods support manipulating multiple values explicitly: |
| 59 | |
| 60 | - :meth:`get_all` returns a list of all values for a header; |
| 61 | - :meth:`raw_items` returns an iterator of ``(name, values)`` pairs. |
| 62 | |
| 63 | """ |
| 64 | |
| 65 | __slots__ = ["_dict", "_list"] |
| 66 | |
| 67 | # Like dict, Headers accepts an optional "mapping or iterable" argument. |
| 68 | def __init__(self, *args: HeadersLike, **kwargs: str) -> None: |
| 69 | self._dict: dict[str, list[str]] = {} |
| 70 | self._list: list[tuple[str, str]] = [] |
| 71 | self.update(*args, **kwargs) |
| 72 | |
| 73 | def __str__(self) -> str: |
| 74 | return "".join(f"{key}: {value}\r\n" for key, value in self._list) + "\r\n" |
| 75 | |
| 76 | def __repr__(self) -> str: |
| 77 | return f"{self.__class__.__name__}({self._list!r})" |
| 78 | |
| 79 | def copy(self) -> Headers: |
| 80 | copy = self.__class__() |
| 81 | copy._dict = self._dict.copy() |
| 82 | copy._list = self._list.copy() |
| 83 | return copy |
| 84 |
no outgoing calls
searching dependent graphs…