|
5 | 5 | # license information.
|
6 | 6 | # --------------------------------------------------------------------------
|
7 | 7 | import datetime
|
8 |
| -from typing import Any, MutableMapping |
| 8 | +from typing import Any, Dict, Iterator, Mapping, MutableMapping |
9 | 9 |
|
10 | 10 |
|
11 | 11 | class _FixedOffset(datetime.tzinfo):
|
@@ -87,26 +87,59 @@ def case_insensitive_dict(*args: Any, **kwargs: Any) -> MutableMapping:
|
87 | 87 | :return: A case-insensitive mutable mapping object.
|
88 | 88 | :rtype: ~collections.abc.MutableMapping
|
89 | 89 | """
|
| 90 | + return CaseInsensitiveDict(*args, **kwargs) |
90 | 91 |
|
91 |
| - # Rational is I don't want to re-implement this, but I don't want |
92 |
| - # to assume "requests" or "aiohttp" are installed either. |
93 |
| - # So I use the one from "requests" or the one from "aiohttp" ("multidict") |
94 |
| - # If one day this library is used in an HTTP context without "requests" nor "aiohttp" installed, |
95 |
| - # we can add "multidict" as a dependency or re-implement our own. |
96 |
| - try: |
97 |
| - from requests.structures import CaseInsensitiveDict |
| 92 | +class CaseInsensitiveDict(MutableMapping): |
| 93 | + """ |
| 94 | + NOTE: This implementation is heavily inspired from the case insensitive dictionary from the requests library. |
| 95 | + Thank you !! |
| 96 | + Case insensitive dictionary implementation. |
| 97 | + The keys are expected to be strings and will be stored in lower case. |
| 98 | + case_insensitive_dict = CaseInsensitiveDict() |
| 99 | + case_insensitive_dict['Key'] = 'some_value' |
| 100 | + case_insensitive_dict['key'] == 'some_value' #True |
| 101 | + """ |
98 | 102 |
|
99 |
| - return CaseInsensitiveDict(*args, **kwargs) |
100 |
| - except ImportError: |
101 |
| - pass |
102 |
| - try: |
103 |
| - # multidict is installed by aiohttp |
104 |
| - from multidict import CIMultiDict |
105 |
| - |
106 |
| - if len(kwargs) == 0 and len(args) == 1 and (not args[0]): |
107 |
| - return CIMultiDict() # in case of case_insensitive_dict(None), we don't want to raise exception |
108 |
| - return CIMultiDict(*args, **kwargs) |
109 |
| - except ImportError: |
110 |
| - raise ValueError( |
111 |
| - "Neither 'requests' or 'multidict' are installed and no case-insensitive dict impl have been found" |
| 103 | + def __init__(self, data=None, **kwargs: Any) -> None: |
| 104 | + self._store: Dict[str, Any] = {} |
| 105 | + if data is None: |
| 106 | + data = {} |
| 107 | + |
| 108 | + self.update(data, **kwargs) |
| 109 | + |
| 110 | + def copy(self) -> "CaseInsensitiveDict": |
| 111 | + return CaseInsensitiveDict(self._store.values()) |
| 112 | + |
| 113 | + def __setitem__(self, key: str, value: str) -> None: |
| 114 | + """ |
| 115 | + Set the `key` to `value`. The original key will be stored with the value |
| 116 | + """ |
| 117 | + self._store[key.lower()] = (key, value) |
| 118 | + |
| 119 | + def __getitem__(self, key: str) -> Any: |
| 120 | + return self._store[key.lower()][1] |
| 121 | + |
| 122 | + def __delitem__(self, key: str) -> None: |
| 123 | + del self._store[key.lower()] |
| 124 | + |
| 125 | + def __iter__(self) -> Iterator[Any]: |
| 126 | + return (key for key, _ in self._store.values()) |
| 127 | + |
| 128 | + def __len__(self) -> int: |
| 129 | + return len(self._store) |
| 130 | + |
| 131 | + def lowerkey_items(self): |
| 132 | + return ( |
| 133 | + (lower_case_key, pair[1]) for lower_case_key, pair in self._store.items() |
112 | 134 | )
|
| 135 | + |
| 136 | + def __eq__(self, other: Any) -> bool: |
| 137 | + if isinstance(other, Mapping): |
| 138 | + other = CaseInsensitiveDict(other) |
| 139 | + else: |
| 140 | + return False |
| 141 | + |
| 142 | + return dict(self.lowerkey_items()) == dict(other.lowerkey_items()) |
| 143 | + |
| 144 | + def __repr__(self) -> str: |
| 145 | + return str(dict(self.items())) |
0 commit comments