1import abc
2from collections.abc import Iterable, Mapping, MutableMapping
3from typing import TYPE_CHECKING, Protocol, TypeVar, Union, overload
4
5if TYPE_CHECKING:
6 from ._multidict_py import istr
7else:
8 istr = str
9
10_V = TypeVar("_V")
11_V_co = TypeVar("_V_co", covariant=True)
12_T = TypeVar("_T")
13
14
15class SupportsKeys(Protocol[_V_co]):
16 def keys(self) -> Iterable[str]: ...
17 def __getitem__(self, key: str, /) -> _V_co: ...
18
19
20class SupportsIKeys(Protocol[_V_co]):
21 def keys(self) -> Iterable[istr]: ...
22 def __getitem__(self, key: istr, /) -> _V_co: ...
23
24
25MDArg = SupportsKeys[_V] | SupportsIKeys[_V] | Iterable[tuple[str, _V]] | None
26
27
28class MultiMapping(Mapping[str, _V_co]):
29 @overload
30 def getall(self, key: str) -> list[_V_co]: ...
31 @overload
32 def getall(self, key: str, default: _T) -> list[_V_co] | _T: ...
33 @abc.abstractmethod
34 def getall(self, key: str, default: _T = ...) -> list[_V_co] | _T:
35 """Return all values for key."""
36
37 @overload
38 def getone(self, key: str) -> _V_co: ...
39 @overload
40 def getone(self, key: str, default: _T) -> _V_co | _T: ...
41 @abc.abstractmethod
42 def getone(self, key: str, default: _T = ...) -> _V_co | _T:
43 """Return first value for key."""
44
45 def to_dict(self) -> dict[str, list[_V_co]]:
46 """Return a dict with lists of all values for each key.
47
48 Deliberately concrete, not abstract: ``MultiMapping`` is public, so
49 requiring a new method would stop every existing subclass outside
50 this project from being instantiated. Subclasses whose keys compare
51 equal under a normalisation the iteration does not apply, such as
52 case-insensitive mappings, must override this; the default would
53 emit one entry per spelling.
54 """
55 result: dict[str, list[_V_co]] = {}
56 for key in self:
57 # Iteration yields a duplicated key once per occurrence, so the
58 # membership test is what keeps getall() from being applied twice.
59 if key not in result:
60 # getall() already returns a fresh list.
61 result[key] = self.getall(key)
62 return result
63
64
65class MutableMultiMapping(MultiMapping[_V], MutableMapping[str, _V]):
66 @abc.abstractmethod
67 def add(self, key: str, value: _V) -> None:
68 """Add value to list."""
69
70 @abc.abstractmethod
71 def extend(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None:
72 """Add everything from arg and kwargs to the mapping."""
73
74 @abc.abstractmethod
75 def merge(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None:
76 """Merge into the mapping, adding non-existing keys."""
77
78 @overload
79 def popone(self, key: str) -> _V: ...
80 @overload
81 def popone(self, key: str, default: _T) -> _V | _T: ...
82 @abc.abstractmethod
83 def popone(self, key: str, default: _T = ...) -> _V | _T:
84 """Remove specified key and return the corresponding value."""
85
86 @overload
87 def popall(self, key: str) -> list[_V]: ...
88 @overload
89 def popall(self, key: str, default: _T) -> list[_V] | _T: ...
90 @abc.abstractmethod
91 def popall(self, key: str, default: _T = ...) -> list[_V] | _T:
92 """Remove all occurrences of key and return the list of corresponding values."""