multidict 6.7.0__cp314-cp314-macosx_10_13_universal2.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of multidict might be problematic. Click here for more details.

@@ -0,0 +1,1242 @@
1
+ import enum
2
+ import functools
3
+ import reprlib
4
+ import sys
5
+ from array import array
6
+ from collections.abc import (
7
+ ItemsView,
8
+ Iterable,
9
+ Iterator,
10
+ KeysView,
11
+ Mapping,
12
+ ValuesView,
13
+ )
14
+ from dataclasses import dataclass
15
+ from typing import (
16
+ TYPE_CHECKING,
17
+ Any,
18
+ ClassVar,
19
+ Generic,
20
+ NoReturn,
21
+ Optional,
22
+ TypeVar,
23
+ Union,
24
+ cast,
25
+ overload,
26
+ )
27
+
28
+ from ._abc import MDArg, MultiMapping, MutableMultiMapping, SupportsKeys
29
+
30
+ if sys.version_info >= (3, 11):
31
+ from typing import Self
32
+ else:
33
+ from typing_extensions import Self
34
+
35
+
36
+ class istr(str):
37
+ """Case insensitive str."""
38
+
39
+ __is_istr__ = True
40
+ __istr_identity__: Optional[str] = None
41
+
42
+
43
+ _V = TypeVar("_V")
44
+ _T = TypeVar("_T")
45
+
46
+ _SENTINEL = enum.Enum("_SENTINEL", "sentinel")
47
+ sentinel = _SENTINEL.sentinel
48
+
49
+ _version = array("Q", [0])
50
+
51
+
52
+ class _Iter(Generic[_T]):
53
+ __slots__ = ("_size", "_iter")
54
+
55
+ def __init__(self, size: int, iterator: Iterator[_T]):
56
+ self._size = size
57
+ self._iter = iterator
58
+
59
+ def __iter__(self) -> Self:
60
+ return self
61
+
62
+ def __next__(self) -> _T:
63
+ return next(self._iter)
64
+
65
+ def __length_hint__(self) -> int:
66
+ return self._size
67
+
68
+
69
+ class _ViewBase(Generic[_V]):
70
+ def __init__(
71
+ self,
72
+ md: "MultiDict[_V]",
73
+ ):
74
+ self._md = md
75
+
76
+ def __len__(self) -> int:
77
+ return len(self._md)
78
+
79
+
80
+ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]):
81
+ def __contains__(self, item: object) -> bool:
82
+ if not isinstance(item, (tuple, list)) or len(item) != 2:
83
+ return False
84
+ key, value = item
85
+ try:
86
+ identity = self._md._identity(key)
87
+ except TypeError:
88
+ return False
89
+ hash_ = hash(identity)
90
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
91
+ if e.identity == identity and value == e.value:
92
+ return True
93
+ return False
94
+
95
+ def __iter__(self) -> _Iter[tuple[str, _V]]:
96
+ return _Iter(len(self), self._iter(self._md._version))
97
+
98
+ def _iter(self, version: int) -> Iterator[tuple[str, _V]]:
99
+ for e in self._md._keys.iter_entries():
100
+ if version != self._md._version:
101
+ raise RuntimeError("Dictionary changed during iteration")
102
+ yield self._md._key(e.key), e.value
103
+
104
+ @reprlib.recursive_repr()
105
+ def __repr__(self) -> str:
106
+ lst = []
107
+ for e in self._md._keys.iter_entries():
108
+ lst.append(f"'{e.key}': {e.value!r}")
109
+ body = ", ".join(lst)
110
+ return f"<{self.__class__.__name__}({body})>"
111
+
112
+ def _parse_item(
113
+ self, arg: Union[tuple[str, _V], _T]
114
+ ) -> Optional[tuple[int, str, str, _V]]:
115
+ if not isinstance(arg, tuple):
116
+ return None
117
+ if len(arg) != 2:
118
+ return None
119
+ try:
120
+ identity = self._md._identity(arg[0])
121
+ return (hash(identity), identity, arg[0], arg[1])
122
+ except TypeError:
123
+ return None
124
+
125
+ def _tmp_set(self, it: Iterable[_T]) -> set[tuple[str, _V]]:
126
+ tmp = set()
127
+ for arg in it:
128
+ item = self._parse_item(arg)
129
+ if item is None:
130
+ continue
131
+ else:
132
+ tmp.add((item[1], item[3]))
133
+ return tmp
134
+
135
+ def __and__(self, other: Iterable[Any]) -> set[tuple[str, _V]]:
136
+ ret = set()
137
+ try:
138
+ it = iter(other)
139
+ except TypeError:
140
+ return NotImplemented
141
+ for arg in it:
142
+ item = self._parse_item(arg)
143
+ if item is None:
144
+ continue
145
+ hash_, identity, key, value = item
146
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
147
+ e.hash = -1
148
+ if e.identity == identity and e.value == value:
149
+ ret.add((e.key, e.value))
150
+ self._md._keys.restore_hash(hash_)
151
+ return ret
152
+
153
+ def __rand__(self, other: Iterable[_T]) -> set[_T]:
154
+ ret = set()
155
+ try:
156
+ it = iter(other)
157
+ except TypeError:
158
+ return NotImplemented
159
+ for arg in it:
160
+ item = self._parse_item(arg)
161
+ if item is None:
162
+ continue
163
+ hash_, identity, key, value = item
164
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
165
+ if e.identity == identity and e.value == value:
166
+ ret.add(arg)
167
+ break
168
+ return ret
169
+
170
+ def __or__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
171
+ ret: set[Union[tuple[str, _V], _T]] = set(self)
172
+ try:
173
+ it = iter(other)
174
+ except TypeError:
175
+ return NotImplemented
176
+ for arg in it:
177
+ item: Optional[tuple[int, str, str, _V]] = self._parse_item(arg)
178
+ if item is None:
179
+ ret.add(arg)
180
+ continue
181
+ hash_, identity, key, value = item
182
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
183
+ if e.identity == identity and e.value == value: # pragma: no branch
184
+ break
185
+ else:
186
+ ret.add(arg)
187
+ return ret
188
+
189
+ def __ror__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
190
+ try:
191
+ ret: set[Union[tuple[str, _V], _T]] = set(other)
192
+ except TypeError:
193
+ return NotImplemented
194
+ tmp = self._tmp_set(ret)
195
+
196
+ for e in self._md._keys.iter_entries():
197
+ if (e.identity, e.value) not in tmp:
198
+ ret.add((e.key, e.value))
199
+ return ret
200
+
201
+ def __sub__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
202
+ ret: set[Union[tuple[str, _V], _T]] = set()
203
+ try:
204
+ it = iter(other)
205
+ except TypeError:
206
+ return NotImplemented
207
+ tmp = self._tmp_set(it)
208
+
209
+ for e in self._md._keys.iter_entries():
210
+ if (e.identity, e.value) not in tmp:
211
+ ret.add((e.key, e.value))
212
+
213
+ return ret
214
+
215
+ def __rsub__(self, other: Iterable[_T]) -> set[_T]:
216
+ ret: set[_T] = set()
217
+ try:
218
+ it = iter(other)
219
+ except TypeError:
220
+ return NotImplemented
221
+ for arg in it:
222
+ item = self._parse_item(arg)
223
+ if item is None:
224
+ ret.add(arg)
225
+ continue
226
+
227
+ hash_, identity, key, value = item
228
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
229
+ if e.identity == identity and e.value == value: # pragma: no branch
230
+ break
231
+ else:
232
+ ret.add(arg)
233
+ return ret
234
+
235
+ def __xor__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]:
236
+ try:
237
+ rgt = set(other)
238
+ except TypeError:
239
+ return NotImplemented
240
+ ret: set[Union[tuple[str, _V], _T]] = self - rgt
241
+ ret |= rgt - self
242
+ return ret
243
+
244
+ __rxor__ = __xor__
245
+
246
+ def isdisjoint(self, other: Iterable[tuple[str, _V]]) -> bool:
247
+ for arg in other:
248
+ item = self._parse_item(arg)
249
+ if item is None:
250
+ continue
251
+
252
+ hash_, identity, key, value = item
253
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
254
+ if e.identity == identity and e.value == value: # pragma: no branch
255
+ return False
256
+ return True
257
+
258
+
259
+ class _ValuesView(_ViewBase[_V], ValuesView[_V]):
260
+ def __contains__(self, value: object) -> bool:
261
+ for e in self._md._keys.iter_entries():
262
+ if e.value == value:
263
+ return True
264
+ return False
265
+
266
+ def __iter__(self) -> _Iter[_V]:
267
+ return _Iter(len(self), self._iter(self._md._version))
268
+
269
+ def _iter(self, version: int) -> Iterator[_V]:
270
+ for e in self._md._keys.iter_entries():
271
+ if version != self._md._version:
272
+ raise RuntimeError("Dictionary changed during iteration")
273
+ yield e.value
274
+
275
+ @reprlib.recursive_repr()
276
+ def __repr__(self) -> str:
277
+ lst = []
278
+ for e in self._md._keys.iter_entries():
279
+ lst.append(repr(e.value))
280
+ body = ", ".join(lst)
281
+ return f"<{self.__class__.__name__}({body})>"
282
+
283
+
284
+ class _KeysView(_ViewBase[_V], KeysView[str]):
285
+ def __contains__(self, key: object) -> bool:
286
+ if not isinstance(key, str):
287
+ return False
288
+ identity = self._md._identity(key)
289
+ hash_ = hash(identity)
290
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
291
+ if e.identity == identity: # pragma: no branch
292
+ return True
293
+ return False
294
+
295
+ def __iter__(self) -> _Iter[str]:
296
+ return _Iter(len(self), self._iter(self._md._version))
297
+
298
+ def _iter(self, version: int) -> Iterator[str]:
299
+ for e in self._md._keys.iter_entries():
300
+ if version != self._md._version:
301
+ raise RuntimeError("Dictionary changed during iteration")
302
+ yield self._md._key(e.key)
303
+
304
+ def __repr__(self) -> str:
305
+ lst = []
306
+ for e in self._md._keys.iter_entries():
307
+ lst.append(f"'{e.key}'")
308
+ body = ", ".join(lst)
309
+ return f"<{self.__class__.__name__}({body})>"
310
+
311
+ def __and__(self, other: Iterable[object]) -> set[str]:
312
+ ret = set()
313
+ try:
314
+ it = iter(other)
315
+ except TypeError:
316
+ return NotImplemented
317
+ for key in it:
318
+ if not isinstance(key, str):
319
+ continue
320
+ identity = self._md._identity(key)
321
+ hash_ = hash(identity)
322
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
323
+ if e.identity == identity: # pragma: no branch
324
+ ret.add(e.key)
325
+ break
326
+ return ret
327
+
328
+ def __rand__(self, other: Iterable[_T]) -> set[_T]:
329
+ ret = set()
330
+ try:
331
+ it = iter(other)
332
+ except TypeError:
333
+ return NotImplemented
334
+ for key in it:
335
+ if not isinstance(key, str):
336
+ continue
337
+ if key in self._md:
338
+ ret.add(key)
339
+ return cast(set[_T], ret)
340
+
341
+ def __or__(self, other: Iterable[_T]) -> set[Union[str, _T]]:
342
+ ret: set[Union[str, _T]] = set(self)
343
+ try:
344
+ it = iter(other)
345
+ except TypeError:
346
+ return NotImplemented
347
+ for key in it:
348
+ if not isinstance(key, str):
349
+ ret.add(key)
350
+ continue
351
+ if key not in self._md:
352
+ ret.add(key)
353
+ return ret
354
+
355
+ def __ror__(self, other: Iterable[_T]) -> set[Union[str, _T]]:
356
+ try:
357
+ ret: set[Union[str, _T]] = set(other)
358
+ except TypeError:
359
+ return NotImplemented
360
+
361
+ tmp = set()
362
+ for key in ret:
363
+ if not isinstance(key, str):
364
+ continue
365
+ identity = self._md._identity(key)
366
+ tmp.add(identity)
367
+
368
+ for e in self._md._keys.iter_entries():
369
+ if e.identity not in tmp:
370
+ ret.add(e.key)
371
+ return ret
372
+
373
+ def __sub__(self, other: Iterable[object]) -> set[str]:
374
+ ret = set(self)
375
+ try:
376
+ it = iter(other)
377
+ except TypeError:
378
+ return NotImplemented
379
+ for key in it:
380
+ if not isinstance(key, str):
381
+ continue
382
+ identity = self._md._identity(key)
383
+ hash_ = hash(identity)
384
+ for slot, idx, e in self._md._keys.iter_hash(hash_):
385
+ if e.identity == identity: # pragma: no branch
386
+ ret.discard(e.key)
387
+ break
388
+ return ret
389
+
390
+ def __rsub__(self, other: Iterable[_T]) -> set[_T]:
391
+ try:
392
+ ret: set[_T] = set(other)
393
+ except TypeError:
394
+ return NotImplemented
395
+ for key in other:
396
+ if not isinstance(key, str):
397
+ continue
398
+ if key in self._md:
399
+ ret.discard(key) # type: ignore[arg-type]
400
+ return ret
401
+
402
+ def __xor__(self, other: Iterable[_T]) -> set[Union[str, _T]]:
403
+ try:
404
+ rgt = set(other)
405
+ except TypeError:
406
+ return NotImplemented
407
+ ret: set[Union[str, _T]] = self - rgt # type: ignore[assignment]
408
+ ret |= rgt - self
409
+ return ret
410
+
411
+ __rxor__ = __xor__
412
+
413
+ def isdisjoint(self, other: Iterable[object]) -> bool:
414
+ for key in other:
415
+ if not isinstance(key, str):
416
+ continue
417
+ if key in self._md:
418
+ return False
419
+ return True
420
+
421
+
422
+ class _CSMixin:
423
+ _ci: ClassVar[bool] = False
424
+
425
+ def _key(self, key: str) -> str:
426
+ return key
427
+
428
+ def _identity(self, key: str) -> str:
429
+ if isinstance(key, str):
430
+ return key
431
+ else:
432
+ raise TypeError("MultiDict keys should be either str or subclasses of str")
433
+
434
+
435
+ class _CIMixin:
436
+ _ci: ClassVar[bool] = True
437
+
438
+ def _key(self, key: str) -> str:
439
+ if type(key) is istr:
440
+ return key
441
+ else:
442
+ return istr(key)
443
+
444
+ def _identity(self, key: str) -> str:
445
+ if isinstance(key, istr):
446
+ ret = key.__istr_identity__
447
+ if ret is None:
448
+ ret = key.lower()
449
+ key.__istr_identity__ = ret
450
+ return ret
451
+ if isinstance(key, str):
452
+ return key.lower()
453
+ else:
454
+ raise TypeError("MultiDict keys should be either str or subclasses of str")
455
+
456
+
457
+ def estimate_log2_keysize(n: int) -> int:
458
+ # 7 == HT_MINSIZE - 1
459
+ return (((n * 3 + 1) // 2) | 7).bit_length()
460
+
461
+
462
+ @dataclass
463
+ class _Entry(Generic[_V]):
464
+ hash: int
465
+ identity: str
466
+ key: str
467
+ value: _V
468
+
469
+
470
+ @dataclass
471
+ class _HtKeys(Generic[_V]): # type: ignore[misc]
472
+ LOG_MINSIZE: ClassVar[int] = 3
473
+ MINSIZE: ClassVar[int] = 8
474
+ PREALLOCATED_INDICES: ClassVar[dict[int, array]] = { # type: ignore[type-arg]
475
+ log2_size: array(
476
+ "b" if log2_size < 8 else "h", (-1 for i in range(1 << log2_size))
477
+ )
478
+ for log2_size in range(3, 10)
479
+ }
480
+
481
+ log2_size: int
482
+ usable: int
483
+
484
+ indices: array # type: ignore[type-arg] # in py3.9 array is not generic
485
+ entries: list[Optional[_Entry[_V]]]
486
+
487
+ @functools.cached_property
488
+ def nslots(self) -> int:
489
+ return 1 << self.log2_size
490
+
491
+ @functools.cached_property
492
+ def mask(self) -> int:
493
+ return self.nslots - 1
494
+
495
+ if sys.implementation.name != "pypy":
496
+
497
+ def __sizeof__(self) -> int:
498
+ return (
499
+ object.__sizeof__(self)
500
+ + sys.getsizeof(self.indices)
501
+ + sys.getsizeof(self.entries)
502
+ )
503
+
504
+ @classmethod
505
+ def new(cls, log2_size: int, entries: list[Optional[_Entry[_V]]]) -> Self:
506
+ size = 1 << log2_size
507
+ usable = (size << 1) // 3
508
+ if log2_size < 10:
509
+ indices = cls.PREALLOCATED_INDICES[log2_size].__copy__()
510
+ elif log2_size < 16:
511
+ indices = array("h", (-1 for i in range(size)))
512
+ elif log2_size < 32:
513
+ indices = array("l", (-1 for i in range(size)))
514
+ else: # pragma: no cover # don't test huge multidicts
515
+ indices = array("q", (-1 for i in range(size)))
516
+ ret = cls(
517
+ log2_size=log2_size,
518
+ usable=usable,
519
+ indices=indices,
520
+ entries=entries,
521
+ )
522
+ return ret
523
+
524
+ def clone(self) -> "_HtKeys[_V]":
525
+ entries = [
526
+ _Entry(e.hash, e.identity, e.key, e.value) if e is not None else None
527
+ for e in self.entries
528
+ ]
529
+
530
+ return _HtKeys(
531
+ log2_size=self.log2_size,
532
+ usable=self.usable,
533
+ indices=self.indices.__copy__(),
534
+ entries=entries,
535
+ )
536
+
537
+ def build_indices(self, update: bool) -> None:
538
+ mask = self.mask
539
+ indices = self.indices
540
+ for idx, e in enumerate(self.entries):
541
+ assert e is not None
542
+ hash_ = e.hash
543
+ if update:
544
+ if hash_ == -1:
545
+ hash_ = hash(e.identity)
546
+ else:
547
+ assert hash_ != -1
548
+ i = hash_ & mask
549
+ perturb = hash_ & sys.maxsize
550
+ while indices[i] != -1:
551
+ perturb >>= 5
552
+ i = mask & (i * 5 + perturb + 1)
553
+ indices[i] = idx
554
+
555
+ def find_empty_slot(self, hash_: int) -> int:
556
+ mask = self.mask
557
+ indices = self.indices
558
+ i = hash_ & mask
559
+ perturb = hash_ & sys.maxsize
560
+ ix = indices[i]
561
+ while ix != -1:
562
+ perturb >>= 5
563
+ i = (i * 5 + perturb + 1) & mask
564
+ ix = indices[i]
565
+ return i
566
+
567
+ def iter_hash(self, hash_: int) -> Iterator[tuple[int, int, _Entry[_V]]]:
568
+ mask = self.mask
569
+ indices = self.indices
570
+ entries = self.entries
571
+ i = hash_ & mask
572
+ perturb = hash_ & sys.maxsize
573
+ ix = indices[i]
574
+ while ix != -1:
575
+ if ix != -2:
576
+ e = entries[ix]
577
+ if e.hash == hash_:
578
+ yield i, ix, e
579
+ perturb >>= 5
580
+ i = (i * 5 + perturb + 1) & mask
581
+ ix = indices[i]
582
+
583
+ def del_idx(self, hash_: int, idx: int) -> None:
584
+ mask = self.mask
585
+ indices = self.indices
586
+ i = hash_ & mask
587
+ perturb = hash_ & sys.maxsize
588
+ ix = indices[i]
589
+ while ix != idx:
590
+ perturb >>= 5
591
+ i = (i * 5 + perturb + 1) & mask
592
+ ix = indices[i]
593
+ indices[i] = -2
594
+
595
+ def iter_entries(self) -> Iterator[_Entry[_V]]:
596
+ return filter(None, self.entries)
597
+
598
+ def restore_hash(self, hash_: int) -> None:
599
+ mask = self.mask
600
+ indices = self.indices
601
+ entries = self.entries
602
+ i = hash_ & mask
603
+ perturb = hash_ & sys.maxsize
604
+ ix = indices[i]
605
+ while ix != -1:
606
+ if ix != -2:
607
+ entry = entries[ix]
608
+ if entry.hash == -1:
609
+ entry.hash = hash_
610
+ perturb >>= 5
611
+ i = (i * 5 + perturb + 1) & mask
612
+ ix = indices[i]
613
+
614
+
615
+ class MultiDict(_CSMixin, MutableMultiMapping[_V]):
616
+ """Dictionary with the support for duplicate keys."""
617
+
618
+ __slots__ = ("_keys", "_used", "_version")
619
+
620
+ def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V):
621
+ self._used = 0
622
+ v = _version
623
+ v[0] += 1
624
+ self._version = v[0]
625
+ if not kwargs:
626
+ md = None
627
+ if isinstance(arg, MultiDictProxy):
628
+ md = arg._md
629
+ elif isinstance(arg, MultiDict):
630
+ md = arg
631
+ if md is not None and md._ci is self._ci:
632
+ self._from_md(md)
633
+ return
634
+
635
+ it = self._parse_args(arg, kwargs)
636
+ log2_size = estimate_log2_keysize(cast(int, next(it)))
637
+ if log2_size > 17: # pragma: no cover
638
+ # Don't overallocate really huge keys space in init
639
+ log2_size = 17
640
+ self._keys: _HtKeys[_V] = _HtKeys.new(log2_size, [])
641
+ self._extend_items(cast(Iterator[_Entry[_V]], it))
642
+
643
+ def _from_md(self, md: "MultiDict[_V]") -> None:
644
+ # Copy everything as-is without compacting the new multidict,
645
+ # otherwise it requires reindexing
646
+ self._keys = md._keys.clone()
647
+ self._used = md._used
648
+
649
+ @overload
650
+ def getall(self, key: str) -> list[_V]: ...
651
+ @overload
652
+ def getall(self, key: str, default: _T) -> Union[list[_V], _T]: ...
653
+ def getall(
654
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
655
+ ) -> Union[list[_V], _T]:
656
+ """Return a list of all values matching the key."""
657
+ identity = self._identity(key)
658
+ hash_ = hash(identity)
659
+ res = []
660
+ restore = []
661
+ for slot, idx, e in self._keys.iter_hash(hash_):
662
+ if e.identity == identity: # pragma: no branch
663
+ res.append(e.value)
664
+ e.hash = -1
665
+ restore.append(idx)
666
+
667
+ if res:
668
+ entries = self._keys.entries
669
+ for idx in restore:
670
+ entries[idx].hash = hash_ # type: ignore[union-attr]
671
+ return res
672
+ if not res and default is not sentinel:
673
+ return default
674
+ raise KeyError("Key not found: %r" % key)
675
+
676
+ @overload
677
+ def getone(self, key: str) -> _V: ...
678
+ @overload
679
+ def getone(self, key: str, default: _T) -> Union[_V, _T]: ...
680
+ def getone(
681
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
682
+ ) -> Union[_V, _T]:
683
+ """Get first value matching the key.
684
+
685
+ Raises KeyError if the key is not found and no default is provided.
686
+ """
687
+ identity = self._identity(key)
688
+ hash_ = hash(identity)
689
+ for slot, idx, e in self._keys.iter_hash(hash_):
690
+ if e.identity == identity: # pragma: no branch
691
+ return e.value
692
+ if default is not sentinel:
693
+ return default
694
+ raise KeyError("Key not found: %r" % key)
695
+
696
+ # Mapping interface #
697
+
698
+ def __getitem__(self, key: str) -> _V:
699
+ return self.getone(key)
700
+
701
+ @overload
702
+ def get(self, key: str, /) -> Union[_V, None]: ...
703
+ @overload
704
+ def get(self, key: str, /, default: _T) -> Union[_V, _T]: ...
705
+ def get(self, key: str, default: Union[_T, None] = None) -> Union[_V, _T, None]:
706
+ """Get first value matching the key.
707
+
708
+ If the key is not found, returns the default (or None if no default is provided)
709
+ """
710
+ return self.getone(key, default)
711
+
712
+ def __iter__(self) -> Iterator[str]:
713
+ return iter(self.keys())
714
+
715
+ def __len__(self) -> int:
716
+ return self._used
717
+
718
+ def keys(self) -> KeysView[str]:
719
+ """Return a new view of the dictionary's keys."""
720
+ return _KeysView(self)
721
+
722
+ def items(self) -> ItemsView[str, _V]:
723
+ """Return a new view of the dictionary's items *(key, value) pairs)."""
724
+ return _ItemsView(self)
725
+
726
+ def values(self) -> _ValuesView[_V]:
727
+ """Return a new view of the dictionary's values."""
728
+ return _ValuesView(self)
729
+
730
+ def __eq__(self, other: object) -> bool:
731
+ if not isinstance(other, Mapping):
732
+ return NotImplemented
733
+ if isinstance(other, MultiDictProxy):
734
+ return self == other._md
735
+ if isinstance(other, MultiDict):
736
+ lft = self._keys
737
+ rht = other._keys
738
+ if self._used != other._used:
739
+ return False
740
+ for e1, e2 in zip(lft.iter_entries(), rht.iter_entries()):
741
+ if e1.identity != e2.identity or e1.value != e2.value:
742
+ return False
743
+ return True
744
+ if self._used != len(other):
745
+ return False
746
+ for k, v in self.items():
747
+ nv = other.get(k, sentinel)
748
+ if v != nv:
749
+ return False
750
+ return True
751
+
752
+ def __contains__(self, key: object) -> bool:
753
+ if not isinstance(key, str):
754
+ return False
755
+ identity = self._identity(key)
756
+ hash_ = hash(identity)
757
+ for slot, idx, e in self._keys.iter_hash(hash_):
758
+ if e.identity == identity: # pragma: no branch
759
+ return True
760
+ return False
761
+
762
+ @reprlib.recursive_repr()
763
+ def __repr__(self) -> str:
764
+ body = ", ".join(f"'{e.key}': {e.value!r}" for e in self._keys.iter_entries())
765
+ return f"<{self.__class__.__name__}({body})>"
766
+
767
+ if sys.implementation.name != "pypy":
768
+
769
+ def __sizeof__(self) -> int:
770
+ return object.__sizeof__(self) + sys.getsizeof(self._keys)
771
+
772
+ def __reduce__(self) -> tuple[type[Self], tuple[list[tuple[str, _V]]]]:
773
+ return (self.__class__, (list(self.items()),))
774
+
775
+ def add(self, key: str, value: _V) -> None:
776
+ identity = self._identity(key)
777
+ hash_ = hash(identity)
778
+ self._add_with_hash(_Entry(hash_, identity, key, value))
779
+ self._incr_version()
780
+
781
+ def copy(self) -> Self:
782
+ """Return a copy of itself."""
783
+ cls = self.__class__
784
+ return cls(self)
785
+
786
+ __copy__ = copy
787
+
788
+ def extend(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None:
789
+ """Extend current MultiDict with more values.
790
+
791
+ This method must be used instead of update.
792
+ """
793
+ it = self._parse_args(arg, kwargs)
794
+ newsize = self._used + cast(int, next(it))
795
+ self._resize(estimate_log2_keysize(newsize), False)
796
+ self._extend_items(cast(Iterator[_Entry[_V]], it))
797
+
798
+ def _parse_args(
799
+ self,
800
+ arg: MDArg[_V],
801
+ kwargs: Mapping[str, _V],
802
+ ) -> Iterator[Union[int, _Entry[_V]]]:
803
+ identity_func = self._identity
804
+ if arg:
805
+ if isinstance(arg, MultiDictProxy):
806
+ arg = arg._md
807
+ if isinstance(arg, MultiDict):
808
+ yield len(arg) + len(kwargs)
809
+ if self._ci is not arg._ci:
810
+ for e in arg._keys.iter_entries():
811
+ identity = identity_func(e.key)
812
+ yield _Entry(hash(identity), identity, e.key, e.value)
813
+ else:
814
+ for e in arg._keys.iter_entries():
815
+ yield _Entry(e.hash, e.identity, e.key, e.value)
816
+ if kwargs:
817
+ for key, value in kwargs.items():
818
+ identity = identity_func(key)
819
+ yield _Entry(hash(identity), identity, key, value)
820
+ else:
821
+ if hasattr(arg, "keys"):
822
+ arg = cast(SupportsKeys[_V], arg)
823
+ arg = [(k, arg[k]) for k in arg.keys()]
824
+ if kwargs:
825
+ arg = list(arg)
826
+ arg.extend(list(kwargs.items()))
827
+ try:
828
+ yield len(arg) + len(kwargs) # type: ignore[arg-type]
829
+ except TypeError:
830
+ yield 0
831
+ for pos, item in enumerate(arg):
832
+ if not len(item) == 2:
833
+ raise ValueError(
834
+ f"multidict update sequence element #{pos}"
835
+ f"has length {len(item)}; 2 is required"
836
+ )
837
+ identity = identity_func(item[0])
838
+ yield _Entry(hash(identity), identity, item[0], item[1])
839
+ else:
840
+ yield len(kwargs)
841
+ for key, value in kwargs.items():
842
+ identity = identity_func(key)
843
+ yield _Entry(hash(identity), identity, key, value)
844
+
845
+ def _extend_items(self, items: Iterable[_Entry[_V]]) -> None:
846
+ for e in items:
847
+ self._add_with_hash(e)
848
+ self._incr_version()
849
+
850
+ def clear(self) -> None:
851
+ """Remove all items from MultiDict."""
852
+ self._used = 0
853
+ self._keys = _HtKeys.new(_HtKeys.LOG_MINSIZE, [])
854
+ self._incr_version()
855
+
856
+ # Mapping interface #
857
+
858
+ def __setitem__(self, key: str, value: _V) -> None:
859
+ identity = self._identity(key)
860
+ hash_ = hash(identity)
861
+ found = False
862
+
863
+ for slot, idx, e in self._keys.iter_hash(hash_):
864
+ if e.identity == identity: # pragma: no branch
865
+ if not found:
866
+ e.key = key
867
+ e.value = value
868
+ e.hash = -1
869
+ found = True
870
+ self._incr_version()
871
+ elif e.hash != -1: # pragma: no branch
872
+ self._del_at(slot, idx)
873
+
874
+ if not found:
875
+ self._add_with_hash(_Entry(hash_, identity, key, value))
876
+ else:
877
+ self._keys.restore_hash(hash_)
878
+
879
+ def __delitem__(self, key: str) -> None:
880
+ found = False
881
+ identity = self._identity(key)
882
+ hash_ = hash(identity)
883
+ for slot, idx, e in self._keys.iter_hash(hash_):
884
+ if e.identity == identity: # pragma: no branch
885
+ self._del_at(slot, idx)
886
+ found = True
887
+ if not found:
888
+ raise KeyError(key)
889
+ else:
890
+ self._incr_version()
891
+
892
+ @overload
893
+ def setdefault(
894
+ self: "MultiDict[Union[_T, None]]", key: str, default: None = None
895
+ ) -> Union[_T, None]: ...
896
+ @overload
897
+ def setdefault(self, key: str, default: _V) -> _V: ...
898
+ def setdefault(self, key: str, default: Union[_V, None] = None) -> Union[_V, None]: # type: ignore[misc]
899
+ """Return value for key, set value to default if key is not present."""
900
+ identity = self._identity(key)
901
+ hash_ = hash(identity)
902
+ for slot, idx, e in self._keys.iter_hash(hash_):
903
+ if e.identity == identity: # pragma: no branch
904
+ return e.value
905
+ self.add(key, default) # type: ignore[arg-type]
906
+ return default
907
+
908
+ @overload
909
+ def popone(self, key: str) -> _V: ...
910
+ @overload
911
+ def popone(self, key: str, default: _T) -> Union[_V, _T]: ...
912
+ def popone(
913
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
914
+ ) -> Union[_V, _T]:
915
+ """Remove specified key and return the corresponding value.
916
+
917
+ If key is not found, d is returned if given, otherwise
918
+ KeyError is raised.
919
+
920
+ """
921
+ identity = self._identity(key)
922
+ hash_ = hash(identity)
923
+ for slot, idx, e in self._keys.iter_hash(hash_):
924
+ if e.identity == identity: # pragma: no branch
925
+ value = e.value
926
+ self._del_at(slot, idx)
927
+ self._incr_version()
928
+ return value
929
+ if default is sentinel:
930
+ raise KeyError(key)
931
+ else:
932
+ return default
933
+
934
+ # Type checking will inherit signature for pop() if we don't confuse it here.
935
+ if not TYPE_CHECKING:
936
+ pop = popone
937
+
938
+ @overload
939
+ def popall(self, key: str) -> list[_V]: ...
940
+ @overload
941
+ def popall(self, key: str, default: _T) -> Union[list[_V], _T]: ...
942
+ def popall(
943
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
944
+ ) -> Union[list[_V], _T]:
945
+ """Remove all occurrences of key and return the list of corresponding
946
+ values.
947
+
948
+ If key is not found, default is returned if given, otherwise
949
+ KeyError is raised.
950
+
951
+ """
952
+ found = False
953
+ identity = self._identity(key)
954
+ hash_ = hash(identity)
955
+ ret = []
956
+ for slot, idx, e in self._keys.iter_hash(hash_):
957
+ if e.identity == identity: # pragma: no branch
958
+ found = True
959
+ ret.append(e.value)
960
+ self._del_at(slot, idx)
961
+ self._incr_version()
962
+
963
+ if not found:
964
+ if default is sentinel:
965
+ raise KeyError(key)
966
+ else:
967
+ return default
968
+ else:
969
+ return ret
970
+
971
+ def popitem(self) -> tuple[str, _V]:
972
+ """Remove and return an arbitrary (key, value) pair."""
973
+ if self._used <= 0:
974
+ raise KeyError("empty multidict")
975
+
976
+ pos = len(self._keys.entries) - 1
977
+ entry = self._keys.entries.pop()
978
+
979
+ while entry is None:
980
+ pos -= 1
981
+ entry = self._keys.entries.pop()
982
+
983
+ ret = self._key(entry.key), entry.value
984
+ self._keys.del_idx(entry.hash, pos)
985
+ self._used -= 1
986
+ self._incr_version()
987
+ return ret
988
+
989
+ def update(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None:
990
+ """Update the dictionary, overwriting existing keys."""
991
+ it = self._parse_args(arg, kwargs)
992
+ newsize = self._used + cast(int, next(it))
993
+ log2_size = estimate_log2_keysize(newsize)
994
+ if log2_size > 17: # pragma: no cover
995
+ # Don't overallocate really huge keys space in update,
996
+ # duplicate keys could reduce the resulting anount of entries
997
+ log2_size = 17
998
+ if log2_size > self._keys.log2_size:
999
+ self._resize(log2_size, False)
1000
+ try:
1001
+ self._update_items(cast(Iterator[_Entry[_V]], it))
1002
+ finally:
1003
+ self._post_update()
1004
+
1005
+ def _update_items(self, items: Iterator[_Entry[_V]]) -> None:
1006
+ for entry in items:
1007
+ found = False
1008
+ hash_ = entry.hash
1009
+ identity = entry.identity
1010
+ for slot, idx, e in self._keys.iter_hash(hash_):
1011
+ if e.identity == identity: # pragma: no branch
1012
+ if not found:
1013
+ found = True
1014
+ e.key = entry.key
1015
+ e.value = entry.value
1016
+ e.hash = -1
1017
+ else:
1018
+ self._del_at_for_upd(e)
1019
+ if not found:
1020
+ self._add_with_hash_for_upd(entry)
1021
+
1022
+ def _post_update(self) -> None:
1023
+ keys = self._keys
1024
+ indices = keys.indices
1025
+ entries = keys.entries
1026
+ for slot in range(keys.nslots):
1027
+ idx = indices[slot]
1028
+ if idx >= 0:
1029
+ e2 = entries[idx]
1030
+ assert e2 is not None
1031
+ if e2.key is None:
1032
+ entries[idx] = None
1033
+ indices[slot] = -2
1034
+ self._used -= 1
1035
+ if e2.hash == -1:
1036
+ e2.hash = hash(e2.identity)
1037
+
1038
+ self._incr_version()
1039
+
1040
+ def merge(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None:
1041
+ """Merge into the dictionary, adding non-existing keys."""
1042
+ it = self._parse_args(arg, kwargs)
1043
+ newsize = self._used + cast(int, next(it))
1044
+ log2_size = estimate_log2_keysize(newsize)
1045
+ if log2_size > 17: # pragma: no cover
1046
+ # Don't overallocate really huge keys space in update,
1047
+ # duplicate keys could reduce the resulting anount of entries
1048
+ log2_size = 17
1049
+ if log2_size > self._keys.log2_size:
1050
+ self._resize(log2_size, False)
1051
+ try:
1052
+ self._merge_items(cast(Iterator[_Entry[_V]], it))
1053
+ finally:
1054
+ self._post_update()
1055
+
1056
+ def _merge_items(self, items: Iterator[_Entry[_V]]) -> None:
1057
+ for entry in items:
1058
+ hash_ = entry.hash
1059
+ identity = entry.identity
1060
+ for slot, idx, e in self._keys.iter_hash(hash_):
1061
+ if e.identity == identity: # pragma: no branch
1062
+ break
1063
+ else:
1064
+ self._add_with_hash_for_upd(entry)
1065
+
1066
+ def _incr_version(self) -> None:
1067
+ v = _version
1068
+ v[0] += 1
1069
+ self._version = v[0]
1070
+
1071
+ def _resize(self, log2_newsize: int, update: bool) -> None:
1072
+ oldkeys = self._keys
1073
+ newentries = self._used
1074
+
1075
+ if len(oldkeys.entries) == newentries:
1076
+ entries = oldkeys.entries
1077
+ else:
1078
+ entries = [e for e in oldkeys.entries if e is not None]
1079
+ newkeys: _HtKeys[_V] = _HtKeys.new(log2_newsize, entries)
1080
+ newkeys.usable -= newentries
1081
+ newkeys.build_indices(update)
1082
+ self._keys = newkeys
1083
+
1084
+ def _add_with_hash(self, entry: _Entry[_V]) -> None:
1085
+ if self._keys.usable <= 0:
1086
+ self._resize((self._used * 3 | _HtKeys.MINSIZE - 1).bit_length(), False)
1087
+ keys = self._keys
1088
+ slot = keys.find_empty_slot(entry.hash)
1089
+ keys.indices[slot] = len(keys.entries)
1090
+ keys.entries.append(entry)
1091
+ self._incr_version()
1092
+ self._used += 1
1093
+ keys.usable -= 1
1094
+
1095
+ def _add_with_hash_for_upd(self, entry: _Entry[_V]) -> None:
1096
+ if self._keys.usable <= 0:
1097
+ self._resize((self._used * 3 | _HtKeys.MINSIZE - 1).bit_length(), True)
1098
+ keys = self._keys
1099
+ slot = keys.find_empty_slot(entry.hash)
1100
+ keys.indices[slot] = len(keys.entries)
1101
+ entry.hash = -1
1102
+ keys.entries.append(entry)
1103
+ self._incr_version()
1104
+ self._used += 1
1105
+ keys.usable -= 1
1106
+
1107
+ def _del_at(self, slot: int, idx: int) -> None:
1108
+ self._keys.entries[idx] = None
1109
+ self._keys.indices[slot] = -2
1110
+ self._used -= 1
1111
+
1112
+ def _del_at_for_upd(self, entry: _Entry[_V]) -> None:
1113
+ entry.key = None # type: ignore[assignment]
1114
+ entry.value = None # type: ignore[assignment]
1115
+
1116
+
1117
+ class CIMultiDict(_CIMixin, MultiDict[_V]):
1118
+ """Dictionary with the support for duplicate case-insensitive keys."""
1119
+
1120
+
1121
+ class MultiDictProxy(_CSMixin, MultiMapping[_V]):
1122
+ """Read-only proxy for MultiDict instance."""
1123
+
1124
+ __slots__ = ("_md",)
1125
+
1126
+ _md: MultiDict[_V]
1127
+
1128
+ def __init__(self, arg: Union[MultiDict[_V], "MultiDictProxy[_V]"]):
1129
+ if not isinstance(arg, (MultiDict, MultiDictProxy)):
1130
+ raise TypeError(
1131
+ f"ctor requires MultiDict or MultiDictProxy instance, not {type(arg)}"
1132
+ )
1133
+ if isinstance(arg, MultiDictProxy):
1134
+ self._md = arg._md
1135
+ else:
1136
+ self._md = arg
1137
+
1138
+ def __reduce__(self) -> NoReturn:
1139
+ raise TypeError(f"can't pickle {self.__class__.__name__} objects")
1140
+
1141
+ @overload
1142
+ def getall(self, key: str) -> list[_V]: ...
1143
+ @overload
1144
+ def getall(self, key: str, default: _T) -> Union[list[_V], _T]: ...
1145
+ def getall(
1146
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
1147
+ ) -> Union[list[_V], _T]:
1148
+ """Return a list of all values matching the key."""
1149
+ if default is not sentinel:
1150
+ return self._md.getall(key, default)
1151
+ else:
1152
+ return self._md.getall(key)
1153
+
1154
+ @overload
1155
+ def getone(self, key: str) -> _V: ...
1156
+ @overload
1157
+ def getone(self, key: str, default: _T) -> Union[_V, _T]: ...
1158
+ def getone(
1159
+ self, key: str, default: Union[_T, _SENTINEL] = sentinel
1160
+ ) -> Union[_V, _T]:
1161
+ """Get first value matching the key.
1162
+
1163
+ Raises KeyError if the key is not found and no default is provided.
1164
+ """
1165
+ if default is not sentinel:
1166
+ return self._md.getone(key, default)
1167
+ else:
1168
+ return self._md.getone(key)
1169
+
1170
+ # Mapping interface #
1171
+
1172
+ def __getitem__(self, key: str) -> _V:
1173
+ return self.getone(key)
1174
+
1175
+ @overload
1176
+ def get(self, key: str, /) -> Union[_V, None]: ...
1177
+ @overload
1178
+ def get(self, key: str, /, default: _T) -> Union[_V, _T]: ...
1179
+ def get(self, key: str, default: Union[_T, None] = None) -> Union[_V, _T, None]:
1180
+ """Get first value matching the key.
1181
+
1182
+ If the key is not found, returns the default (or None if no default is provided)
1183
+ """
1184
+ return self._md.getone(key, default)
1185
+
1186
+ def __iter__(self) -> Iterator[str]:
1187
+ return iter(self._md.keys())
1188
+
1189
+ def __len__(self) -> int:
1190
+ return len(self._md)
1191
+
1192
+ def keys(self) -> KeysView[str]:
1193
+ """Return a new view of the dictionary's keys."""
1194
+ return self._md.keys()
1195
+
1196
+ def items(self) -> ItemsView[str, _V]:
1197
+ """Return a new view of the dictionary's items *(key, value) pairs)."""
1198
+ return self._md.items()
1199
+
1200
+ def values(self) -> _ValuesView[_V]:
1201
+ """Return a new view of the dictionary's values."""
1202
+ return self._md.values()
1203
+
1204
+ def __eq__(self, other: object) -> bool:
1205
+ return self._md == other
1206
+
1207
+ def __contains__(self, key: object) -> bool:
1208
+ return key in self._md
1209
+
1210
+ @reprlib.recursive_repr()
1211
+ def __repr__(self) -> str:
1212
+ body = ", ".join(f"'{k}': {v!r}" for k, v in self.items())
1213
+ return f"<{self.__class__.__name__}({body})>"
1214
+
1215
+ def copy(self) -> MultiDict[_V]:
1216
+ """Return a copy of itself."""
1217
+ return MultiDict(self._md)
1218
+
1219
+
1220
+ class CIMultiDictProxy(_CIMixin, MultiDictProxy[_V]):
1221
+ """Read-only proxy for CIMultiDict instance."""
1222
+
1223
+ def __init__(self, arg: Union[MultiDict[_V], MultiDictProxy[_V]]):
1224
+ if not isinstance(arg, (CIMultiDict, CIMultiDictProxy)):
1225
+ raise TypeError(
1226
+ "ctor requires CIMultiDict or CIMultiDictProxy instance"
1227
+ f", not {type(arg)}"
1228
+ )
1229
+
1230
+ super().__init__(arg)
1231
+
1232
+ def copy(self) -> CIMultiDict[_V]:
1233
+ """Return a copy of itself."""
1234
+ return CIMultiDict(self._md)
1235
+
1236
+
1237
+ def getversion(md: Union[MultiDict[object], MultiDictProxy[object]]) -> int:
1238
+ if isinstance(md, MultiDictProxy):
1239
+ md = md._md
1240
+ elif not isinstance(md, MultiDict):
1241
+ raise TypeError("Parameter should be multidict or proxy")
1242
+ return md._version