Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/werkzeug/datastructures/structures.py: 43%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

393 statements  

1from __future__ import annotations 

2 

3import collections.abc as cabc 

4import typing as t 

5from copy import deepcopy 

6 

7from .. import exceptions 

8from .._internal import _missing 

9from ..http import dump_header 

10from ..http import parse_list_header 

11from .mixins import ImmutableDictMixin 

12from .mixins import ImmutableListMixin 

13from .mixins import ImmutableMultiDictMixin 

14from .mixins import UpdateDictMixin 

15 

16if t.TYPE_CHECKING: 

17 import typing_extensions as te 

18 

19K = t.TypeVar("K") 

20V = t.TypeVar("V") 

21T = t.TypeVar("T") 

22 

23 

24def iter_multi_items( 

25 mapping: ( 

26 MultiDict[K, V] 

27 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]] 

28 | cabc.Iterable[tuple[K, V]] 

29 ), 

30) -> cabc.Iterator[tuple[K, V]]: 

31 """Iterates over the items of a mapping yielding keys and values 

32 without dropping any from more complex structures. 

33 """ 

34 if isinstance(mapping, MultiDict): 

35 yield from mapping.items(multi=True) 

36 elif isinstance(mapping, cabc.Mapping): 

37 for key, value in mapping.items(): 

38 if isinstance(value, (list, tuple, set)): 

39 for v in value: 

40 yield key, v 

41 else: 

42 yield key, value 

43 else: 

44 yield from mapping 

45 

46 

47class ImmutableList(ImmutableListMixin, list[V]): # type: ignore[misc] 

48 """An immutable :class:`list`. 

49 

50 .. versionadded:: 0.5 

51 

52 :private: 

53 """ 

54 

55 def __repr__(self) -> str: 

56 return f"{type(self).__name__}({list.__repr__(self)})" 

57 

58 

59class TypeConversionDict(dict[K, V]): 

60 """Works like a regular dict but the :meth:`get` method can perform 

61 type conversions. :class:`MultiDict` and :class:`CombinedMultiDict` 

62 are subclasses of this class and provide the same feature. 

63 

64 .. versionadded:: 0.5 

65 """ 

66 

67 @t.overload # type: ignore[override] 

68 def get(self, key: K) -> V | None: ... 

69 @t.overload 

70 def get(self, key: K, default: V) -> V: ... 

71 @t.overload 

72 def get(self, key: K, default: T) -> V | T: ... 

73 @t.overload 

74 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ... 

75 @t.overload 

76 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ... 

77 def get( # type: ignore[misc] 

78 self, 

79 key: K, 

80 default: V | T | None = None, 

81 type: cabc.Callable[[V], T] | None = None, 

82 ) -> V | T | None: 

83 """Return the default value if the requested data doesn't exist. 

84 If `type` is provided and is a callable it should convert the value, 

85 return it or raise a :exc:`ValueError` if that is not possible. In 

86 this case the function will return the default as if the value was not 

87 found: 

88 

89 >>> d = TypeConversionDict(foo='42', bar='blub') 

90 >>> d.get('foo', type=int) 

91 42 

92 >>> d.get('bar', -1, type=int) 

93 -1 

94 

95 :param key: The key to be looked up. 

96 :param default: The default value to be returned if the key can't 

97 be looked up. If not further specified `None` is 

98 returned. 

99 :param type: A callable that is used to cast the value in the 

100 :class:`MultiDict`. If a :exc:`ValueError` or a 

101 :exc:`TypeError` is raised by this callable the default 

102 value is returned. 

103 

104 .. versionchanged:: 3.0.2 

105 Returns the default value on :exc:`TypeError`, too. 

106 """ 

107 try: 

108 rv = self[key] 

109 except KeyError: 

110 return default 

111 

112 if type is None: 

113 return rv 

114 

115 try: 

116 return type(rv) 

117 except (ValueError, TypeError): 

118 return default 

119 

120 

121class ImmutableTypeConversionDict(ImmutableDictMixin[K, V], TypeConversionDict[K, V]): # type: ignore[misc] 

122 """Works like a :class:`TypeConversionDict` but does not support 

123 modifications. 

124 

125 .. versionadded:: 0.5 

126 """ 

127 

128 def copy(self) -> TypeConversionDict[K, V]: 

129 """Return a shallow mutable copy of this object. Keep in mind that 

130 the standard library's :func:`copy` function is a no-op for this class 

131 like for any other python immutable type (eg: :class:`tuple`). 

132 """ 

133 return TypeConversionDict(self) 

134 

135 def __copy__(self) -> te.Self: 

136 return self 

137 

138 

139class MultiDict(TypeConversionDict[K, V]): 

140 """A :class:`MultiDict` is a dictionary subclass customized to deal with 

141 multiple values for the same key which is for example used by the parsing 

142 functions in the wrappers. This is necessary because some HTML form 

143 elements pass multiple values for the same key. 

144 

145 :class:`MultiDict` implements all standard dictionary methods. 

146 Internally, it saves all values for a key as a list, but the standard dict 

147 access methods will only return the first value for a key. If you want to 

148 gain access to the other values, too, you have to use the `list` methods as 

149 explained below. 

150 

151 Basic Usage: 

152 

153 >>> d = MultiDict([('a', 'b'), ('a', 'c')]) 

154 >>> d 

155 MultiDict([('a', 'b'), ('a', 'c')]) 

156 >>> d['a'] 

157 'b' 

158 >>> d.getlist('a') 

159 ['b', 'c'] 

160 >>> 'a' in d 

161 True 

162 

163 It behaves like a normal dict thus all dict functions will only return the 

164 first value when multiple values for one key are found. 

165 

166 From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a 

167 subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will 

168 render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP 

169 exceptions. 

170 

171 A :class:`MultiDict` can be constructed from an iterable of 

172 ``(key, value)`` tuples, a dict, a :class:`MultiDict` or from Werkzeug 0.2 

173 onwards some keyword parameters. 

174 

175 :param mapping: the initial value for the :class:`MultiDict`. Either a 

176 regular dict, an iterable of ``(key, value)`` tuples 

177 or `None`. 

178 

179 .. versionchanged:: 3.1 

180 Implement ``|`` and ``|=`` operators. 

181 """ 

182 

183 def __init__( 

184 self, 

185 mapping: ( 

186 MultiDict[K, V] 

187 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]] 

188 | cabc.Iterable[tuple[K, V]] 

189 | None 

190 ) = None, 

191 ) -> None: 

192 if mapping is None: 

193 super().__init__() 

194 elif isinstance(mapping, MultiDict): 

195 super().__init__((k, vs[:]) for k, vs in mapping.lists()) # type: ignore[misc] 

196 elif isinstance(mapping, cabc.Mapping): 

197 tmp = {} 

198 for key, value in mapping.items(): 

199 if isinstance(value, (list, tuple, set)): 

200 value = list(value) 

201 

202 if not value: 

203 continue 

204 else: 

205 value = [value] 

206 tmp[key] = value 

207 super().__init__(tmp) # type: ignore[arg-type] 

208 else: 

209 tmp = {} 

210 for key, value in mapping: 

211 tmp.setdefault(key, []).append(value) 

212 super().__init__(tmp) # type: ignore[arg-type] 

213 

214 def __getstate__(self) -> t.Any: 

215 return dict(self.lists()) 

216 

217 def __setstate__(self, value: t.Any) -> None: 

218 super().clear() 

219 super().update(value) 

220 

221 def __iter__(self) -> cabc.Iterator[K]: 

222 # https://github.com/python/cpython/issues/87412 

223 # If __iter__ is not overridden, Python uses a fast path for dict(md), 

224 # taking the data directly and getting lists of values, rather than 

225 # calling __getitem__ and getting only the first value. 

226 return super().__iter__() 

227 

228 def __getitem__(self, key: K) -> V: 

229 """Return the first data value for this key; 

230 raises KeyError if not found. 

231 

232 :param key: The key to be looked up. 

233 :raise KeyError: if the key does not exist. 

234 """ 

235 

236 if key in self: 

237 lst = super().__getitem__(key) 

238 if len(lst) > 0: # type: ignore[arg-type] 

239 return lst[0] # type: ignore[index,no-any-return] 

240 raise exceptions.BadRequestKeyError(key) 

241 

242 def __setitem__(self, key: K, value: V) -> None: 

243 """Like :meth:`add` but removes an existing key first. 

244 

245 :param key: the key for the value. 

246 :param value: the value to set. 

247 """ 

248 super().__setitem__(key, [value]) # type: ignore[assignment] 

249 

250 def add(self, key: K, value: V) -> None: 

251 """Adds a new value for the key. 

252 

253 .. versionadded:: 0.6 

254 

255 :param key: the key for the value. 

256 :param value: the value to add. 

257 """ 

258 super().setdefault(key, []).append(value) # type: ignore[arg-type,attr-defined] 

259 

260 @t.overload 

261 def getlist(self, key: K) -> list[V]: ... 

262 @t.overload 

263 def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ... 

264 def getlist( 

265 self, key: K, type: cabc.Callable[[V], T] | None = None 

266 ) -> list[V] | list[T]: 

267 """Return the list of items for a given key. If that key is not in the 

268 `MultiDict`, the return value will be an empty list. Just like `get`, 

269 `getlist` accepts a `type` parameter. All items will be converted 

270 with the callable defined there. 

271 

272 :param key: The key to be looked up. 

273 :param type: Callable to convert each value. If a ``ValueError`` or 

274 ``TypeError`` is raised, the value is omitted. 

275 :return: a :class:`list` of all the values for the key. 

276 

277 .. versionchanged:: 3.1 

278 Catches ``TypeError`` in addition to ``ValueError``. 

279 """ 

280 try: 

281 rv: list[V] = super().__getitem__(key) # type: ignore[assignment] 

282 except KeyError: 

283 return [] 

284 if type is None: 

285 return list(rv) 

286 result = [] 

287 for item in rv: 

288 try: 

289 result.append(type(item)) 

290 except (ValueError, TypeError): 

291 pass 

292 return result 

293 

294 def setlist(self, key: K, new_list: cabc.Iterable[V]) -> None: 

295 """Remove the old values for a key and add new ones. Note that the list 

296 you pass the values in will be shallow-copied before it is inserted in 

297 the dictionary. 

298 

299 >>> d = MultiDict() 

300 >>> d.setlist('foo', ['1', '2']) 

301 >>> d['foo'] 

302 '1' 

303 >>> d.getlist('foo') 

304 ['1', '2'] 

305 

306 :param key: The key for which the values are set. 

307 :param new_list: An iterable with the new values for the key. Old values 

308 are removed first. 

309 """ 

310 super().__setitem__(key, list(new_list)) # type: ignore[assignment] 

311 

312 @t.overload 

313 def setdefault(self, key: K) -> None: ... 

314 @t.overload 

315 def setdefault(self, key: K, default: V) -> V: ... 

316 def setdefault(self, key: K, default: V | None = None) -> V | None: 

317 """Returns the value for the key if it is in the dict, otherwise it 

318 returns `default` and sets that value for `key`. 

319 

320 :param key: The key to be looked up. 

321 :param default: The default value to be returned if the key is not 

322 in the dict. If not further specified it's `None`. 

323 """ 

324 if key not in self: 

325 self[key] = default # type: ignore[assignment] 

326 

327 return self[key] 

328 

329 def setlistdefault( 

330 self, key: K, default_list: cabc.Iterable[V] | None = None 

331 ) -> list[V]: 

332 """Like `setdefault` but sets multiple values. The list returned 

333 is not a copy, but the list that is actually used internally. This 

334 means that you can put new values into the dict by appending items 

335 to the list: 

336 

337 >>> d = MultiDict({"foo": 1}) 

338 >>> d.setlistdefault("foo").extend([2, 3]) 

339 >>> d.getlist("foo") 

340 [1, 2, 3] 

341 

342 :param key: The key to be looked up. 

343 :param default_list: An iterable of default values. It is either copied 

344 (in case it was a list) or converted into a list 

345 before returned. 

346 :return: a :class:`list` 

347 """ 

348 if key not in self: 

349 super().__setitem__(key, list(default_list or ())) # type: ignore[assignment] 

350 

351 return super().__getitem__(key) # type: ignore[return-value] 

352 

353 def items(self, multi: bool = False) -> cabc.Iterable[tuple[K, V]]: # type: ignore[override] 

354 """Return an iterator of ``(key, value)`` pairs. 

355 

356 :param multi: If set to `True` the iterator returned will have a pair 

357 for each value of each key. Otherwise it will only 

358 contain pairs for the first value of each key. 

359 """ 

360 values: list[V] 

361 

362 for key, values in super().items(): # type: ignore[assignment] 

363 if multi: 

364 for value in values: 

365 yield key, value 

366 else: 

367 yield key, values[0] 

368 

369 def lists(self) -> cabc.Iterable[tuple[K, list[V]]]: 

370 """Return a iterator of ``(key, values)`` pairs, where values is the list 

371 of all values associated with the key.""" 

372 values: list[V] 

373 

374 for key, values in super().items(): # type: ignore[assignment] 

375 yield key, list(values) 

376 

377 def values(self) -> cabc.Iterable[V]: # type: ignore[override] 

378 """Returns an iterator of the first value on every key's value list.""" 

379 values: list[V] 

380 

381 for values in super().values(): # type: ignore[assignment] 

382 yield values[0] 

383 

384 def listvalues(self) -> cabc.Iterable[list[V]]: 

385 """Return an iterator of all values associated with a key. Zipping 

386 :meth:`keys` and this is the same as calling :meth:`lists`: 

387 

388 >>> d = MultiDict({"foo": [1, 2, 3]}) 

389 >>> zip(d.keys(), d.listvalues()) == d.lists() 

390 True 

391 """ 

392 return super().values() # type: ignore[return-value] 

393 

394 def copy(self) -> te.Self: 

395 """Return a shallow copy of this object.""" 

396 return self.__class__(self) 

397 

398 def deepcopy(self, memo: t.Any = None) -> te.Self: 

399 """Return a deep copy of this object.""" 

400 return self.__class__(deepcopy(self.to_dict(flat=False), memo)) 

401 

402 @t.overload 

403 def to_dict(self, flat: t.Literal[True] = ...) -> dict[K, V]: ... 

404 @t.overload 

405 def to_dict(self, flat: t.Literal[False]) -> dict[K, list[V]]: ... 

406 def to_dict(self, flat: bool = True) -> dict[K, V] | dict[K, list[V]]: 

407 """Return the contents as regular dict. If `flat` is `True` the 

408 returned dict will only have the first item present, if `flat` is 

409 `False` all values will be returned as lists. 

410 

411 :param flat: If set to `False` the dict returned will have lists 

412 with all the values in it. Otherwise it will only 

413 contain the first value for each key. 

414 :return: a :class:`dict` 

415 """ 

416 if flat: 

417 return dict(self.items()) 

418 return dict(self.lists()) 

419 

420 def update( # type: ignore[override] 

421 self, 

422 mapping: ( 

423 MultiDict[K, V] 

424 | cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]] 

425 | cabc.Iterable[tuple[K, V]] 

426 ), 

427 ) -> None: 

428 """update() extends rather than replaces existing key lists: 

429 

430 >>> a = MultiDict({'x': 1}) 

431 >>> b = MultiDict({'x': 2, 'y': 3}) 

432 >>> a.update(b) 

433 >>> a 

434 MultiDict([('y', 3), ('x', 1), ('x', 2)]) 

435 

436 If the value list for a key in ``other_dict`` is empty, no new values 

437 will be added to the dict and the key will not be created: 

438 

439 >>> x = {'empty_list': []} 

440 >>> y = MultiDict() 

441 >>> y.update(x) 

442 >>> y 

443 MultiDict([]) 

444 """ 

445 for key, value in iter_multi_items(mapping): 

446 self.add(key, value) 

447 

448 def __or__( # type: ignore[override] 

449 self, other: cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]] 

450 ) -> MultiDict[K, V]: 

451 if not isinstance(other, cabc.Mapping): 

452 return NotImplemented 

453 

454 rv = self.copy() 

455 rv.update(other) 

456 return rv 

457 

458 def __ior__( # type: ignore[override] 

459 self, 

460 other: ( 

461 cabc.Mapping[K, V | list[V] | tuple[V, ...] | set[V]] 

462 | cabc.Iterable[tuple[K, V]] 

463 ), 

464 ) -> te.Self: 

465 if not isinstance(other, (cabc.Mapping, cabc.Iterable)): 

466 return NotImplemented 

467 

468 self.update(other) 

469 return self 

470 

471 @t.overload 

472 def pop(self, key: K) -> V: ... 

473 @t.overload 

474 def pop(self, key: K, default: V) -> V: ... 

475 @t.overload 

476 def pop(self, key: K, default: T) -> V | T: ... 

477 def pop( 

478 self, 

479 key: K, 

480 default: V | T = _missing, # type: ignore[assignment] 

481 ) -> V | T: 

482 """Pop the first item for a list on the dict. Afterwards the 

483 key is removed from the dict, so additional values are discarded: 

484 

485 >>> d = MultiDict({"foo": [1, 2, 3]}) 

486 >>> d.pop("foo") 

487 1 

488 >>> "foo" in d 

489 False 

490 

491 :param key: the key to pop. 

492 :param default: if provided the value to return if the key was 

493 not in the dictionary. 

494 """ 

495 lst: list[V] 

496 

497 try: 

498 lst = super().pop(key) # type: ignore[assignment] 

499 

500 if len(lst) == 0: 

501 raise exceptions.BadRequestKeyError(key) 

502 

503 return lst[0] 

504 except KeyError: 

505 if default is not _missing: 

506 return default 

507 

508 raise exceptions.BadRequestKeyError(key) from None 

509 

510 def popitem(self) -> tuple[K, V]: 

511 """Pop an item from the dict.""" 

512 item: tuple[K, list[V]] 

513 

514 try: 

515 item = super().popitem() # type: ignore[assignment] 

516 

517 if len(item[1]) == 0: 

518 raise exceptions.BadRequestKeyError(item[0]) 

519 

520 return item[0], item[1][0] 

521 except KeyError as e: 

522 raise exceptions.BadRequestKeyError(e.args[0]) from None 

523 

524 def poplist(self, key: K) -> list[V]: 

525 """Pop the list for a key from the dict. If the key is not in the dict 

526 an empty list is returned. 

527 

528 .. versionchanged:: 0.5 

529 If the key does no longer exist a list is returned instead of 

530 raising an error. 

531 """ 

532 return super().pop(key, []) # type: ignore[return-value] 

533 

534 def popitemlist(self) -> tuple[K, list[V]]: 

535 """Pop a ``(key, list)`` tuple from the dict.""" 

536 try: 

537 return super().popitem() # type: ignore[return-value] 

538 except KeyError as e: 

539 raise exceptions.BadRequestKeyError(e.args[0]) from None 

540 

541 def __copy__(self) -> te.Self: 

542 return self.copy() 

543 

544 def __deepcopy__(self, memo: t.Any) -> te.Self: 

545 return self.deepcopy(memo=memo) 

546 

547 def __repr__(self) -> str: 

548 return f"{type(self).__name__}({list(self.items(multi=True))!r})" 

549 

550 

551class CombinedMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc] 

552 """A read only :class:`MultiDict` that you can pass multiple :class:`MultiDict` 

553 instances as sequence and it will combine the return values of all wrapped 

554 dicts: 

555 

556 >>> from werkzeug.datastructures import CombinedMultiDict, MultiDict 

557 >>> post = MultiDict([('foo', 'bar')]) 

558 >>> get = MultiDict([('blub', 'blah')]) 

559 >>> combined = CombinedMultiDict([get, post]) 

560 >>> combined['foo'] 

561 'bar' 

562 >>> combined['blub'] 

563 'blah' 

564 

565 This works for all read operations and will raise a `TypeError` for 

566 methods that usually change data which isn't possible. 

567 

568 From Werkzeug 0.3 onwards, the `KeyError` raised by this class is also a 

569 subclass of the :exc:`~exceptions.BadRequest` HTTP exception and will 

570 render a page for a ``400 BAD REQUEST`` if caught in a catch-all for HTTP 

571 exceptions. 

572 """ 

573 

574 def __reduce_ex__(self, protocol: t.SupportsIndex) -> t.Any: 

575 return type(self), (self.dicts,) 

576 

577 def __init__(self, dicts: cabc.Iterable[MultiDict[K, V]] | None = None) -> None: 

578 super().__init__() 

579 self.dicts: list[MultiDict[K, V]] = list(dicts or ()) 

580 

581 @classmethod 

582 def fromkeys(cls, keys: t.Any, value: t.Any = None) -> t.NoReturn: 

583 raise TypeError(f"cannot create {cls.__name__!r} instances by fromkeys") 

584 

585 def __getitem__(self, key: K) -> V: 

586 for d in self.dicts: 

587 if key in d: 

588 return d[key] 

589 raise exceptions.BadRequestKeyError(key) 

590 

591 @t.overload # type: ignore[override] 

592 def get(self, key: K) -> V | None: ... 

593 @t.overload 

594 def get(self, key: K, default: V) -> V: ... 

595 @t.overload 

596 def get(self, key: K, default: T) -> V | T: ... 

597 @t.overload 

598 def get(self, key: str, type: cabc.Callable[[V], T]) -> T | None: ... 

599 @t.overload 

600 def get(self, key: str, default: T, type: cabc.Callable[[V], T]) -> T: ... 

601 def get( # type: ignore[misc] 

602 self, 

603 key: K, 

604 default: V | T | None = None, 

605 type: cabc.Callable[[V], T] | None = None, 

606 ) -> V | T | None: 

607 for d in self.dicts: 

608 if key in d: 

609 if type is not None: 

610 try: 

611 return type(d[key]) 

612 except (ValueError, TypeError): 

613 continue 

614 return d[key] 

615 return default 

616 

617 @t.overload 

618 def getlist(self, key: K) -> list[V]: ... 

619 @t.overload 

620 def getlist(self, key: K, type: cabc.Callable[[V], T]) -> list[T]: ... 

621 def getlist( 

622 self, key: K, type: cabc.Callable[[V], T] | None = None 

623 ) -> list[V] | list[T]: 

624 rv = [] 

625 for d in self.dicts: 

626 rv.extend(d.getlist(key, type)) # type: ignore[arg-type] 

627 return rv 

628 

629 def _keys_impl(self) -> set[K]: 

630 """This function exists so __len__ can be implemented more efficiently, 

631 saving one list creation from an iterator. 

632 """ 

633 return set(k for d in self.dicts for k in d) 

634 

635 def keys(self) -> cabc.Iterable[K]: # type: ignore[override] 

636 return self._keys_impl() 

637 

638 def __iter__(self) -> cabc.Iterator[K]: 

639 return iter(self._keys_impl()) 

640 

641 @t.overload # type: ignore[override] 

642 def items(self) -> cabc.Iterable[tuple[K, V]]: ... 

643 @t.overload 

644 def items(self, multi: t.Literal[True]) -> cabc.Iterable[tuple[K, list[V]]]: ... 

645 def items( 

646 self, multi: bool = False 

647 ) -> cabc.Iterable[tuple[K, V]] | cabc.Iterable[tuple[K, list[V]]]: 

648 found = set() 

649 for d in self.dicts: 

650 for key, value in d.items(multi): 

651 if multi: 

652 yield key, value 

653 elif key not in found: 

654 found.add(key) 

655 yield key, value 

656 

657 def values(self) -> cabc.Iterable[V]: # type: ignore[override] 

658 for _, value in self.items(): 

659 yield value 

660 

661 def lists(self) -> cabc.Iterable[tuple[K, list[V]]]: 

662 rv: dict[K, list[V]] = {} 

663 for d in self.dicts: 

664 for key, values in d.lists(): 

665 rv.setdefault(key, []).extend(values) 

666 return rv.items() 

667 

668 def listvalues(self) -> cabc.Iterable[list[V]]: 

669 return (x[1] for x in self.lists()) 

670 

671 def copy(self) -> MultiDict[K, V]: # type: ignore[override] 

672 """Return a shallow mutable copy of this object. 

673 

674 This returns a :class:`MultiDict` representing the data at the 

675 time of copying. The copy will no longer reflect changes to the 

676 wrapped dicts. 

677 

678 .. versionchanged:: 0.15 

679 Return a mutable :class:`MultiDict`. 

680 """ 

681 return MultiDict(self) 

682 

683 def __len__(self) -> int: 

684 return len(self._keys_impl()) 

685 

686 def __contains__(self, key: K) -> bool: # type: ignore[override] 

687 for d in self.dicts: 

688 if key in d: 

689 return True 

690 return False 

691 

692 def __repr__(self) -> str: 

693 return f"{type(self).__name__}({self.dicts!r})" 

694 

695 

696class ImmutableDict(ImmutableDictMixin[K, V], dict[K, V]): # type: ignore[misc] 

697 """An immutable :class:`dict`. 

698 

699 .. versionadded:: 0.5 

700 """ 

701 

702 def __repr__(self) -> str: 

703 return f"{type(self).__name__}({dict.__repr__(self)})" 

704 

705 def copy(self) -> dict[K, V]: 

706 """Return a shallow mutable copy of this object. Keep in mind that 

707 the standard library's :func:`copy` function is a no-op for this class 

708 like for any other python immutable type (eg: :class:`tuple`). 

709 """ 

710 return dict(self) 

711 

712 def __copy__(self) -> te.Self: 

713 return self 

714 

715 

716class ImmutableMultiDict(ImmutableMultiDictMixin[K, V], MultiDict[K, V]): # type: ignore[misc] 

717 """An immutable :class:`MultiDict`. 

718 

719 .. versionadded:: 0.5 

720 """ 

721 

722 def copy(self) -> MultiDict[K, V]: # type: ignore[override] 

723 """Return a shallow mutable copy of this object. Keep in mind that 

724 the standard library's :func:`copy` function is a no-op for this class 

725 like for any other python immutable type (eg: :class:`tuple`). 

726 """ 

727 return MultiDict(self) 

728 

729 def __copy__(self) -> te.Self: 

730 return self 

731 

732 

733class CallbackDict(UpdateDictMixin[K, V], dict[K, V]): 

734 """A dict that calls a function passed every time something is changed. 

735 The function is passed the dict instance. 

736 """ 

737 

738 def __init__( 

739 self, 

740 initial: cabc.Mapping[K, V] | cabc.Iterable[tuple[K, V]] | None = None, 

741 on_update: cabc.Callable[[te.Self], None] | None = None, 

742 ) -> None: 

743 if initial is None: 

744 super().__init__() 

745 else: 

746 super().__init__(initial) 

747 

748 self.on_update = on_update 

749 

750 def __repr__(self) -> str: 

751 return f"<{type(self).__name__} {super().__repr__()}>" 

752 

753 

754class HeaderSet(cabc.MutableSet[str]): 

755 """Similar to the :class:`ETags` class this implements a set-like structure. 

756 Unlike :class:`ETags` this is case insensitive and used for vary, allow, and 

757 content-language headers. 

758 

759 If not constructed using the :func:`parse_set_header` function the 

760 instantiation works like this: 

761 

762 >>> hs = HeaderSet(['foo', 'bar', 'baz']) 

763 >>> hs 

764 HeaderSet(['foo', 'bar', 'baz']) 

765 

766 .. versionchanged:: 3.2 

767 The ``on_update`` parameter was removed. 

768 """ 

769 

770 def __init__(self, headers: cabc.Iterable[str] | None = None) -> None: 

771 self._headers = list(headers or ()) 

772 self._set = {x.lower() for x in self._headers} 

773 self._on_update: cabc.Callable[[HeaderSet], None] | None = None 

774 

775 def add(self, header: str) -> None: 

776 """Add a new header to the set.""" 

777 self.update((header,)) 

778 

779 def remove(self: te.Self, header: str) -> None: 

780 """Remove a header from the set. This raises an :exc:`KeyError` if the 

781 header is not in the set. 

782 

783 .. versionchanged:: 0.5 

784 In older versions a :exc:`IndexError` was raised instead of a 

785 :exc:`KeyError` if the object was missing. 

786 

787 :param header: the header to be removed. 

788 """ 

789 key = header.lower() 

790 if key not in self._set: 

791 raise KeyError(header) 

792 self._set.remove(key) 

793 for idx, key in enumerate(self._headers): 

794 if key.lower() == header: 

795 del self._headers[idx] 

796 break 

797 if self._on_update is not None: 

798 self._on_update(self) 

799 

800 def update(self: te.Self, iterable: cabc.Iterable[str]) -> None: 

801 """Add all the headers from the iterable to the set. 

802 

803 :param iterable: updates the set with the items from the iterable. 

804 """ 

805 inserted_any = False 

806 for header in iterable: 

807 key = header.lower() 

808 if key not in self._set: 

809 self._headers.append(header) 

810 self._set.add(key) 

811 inserted_any = True 

812 if inserted_any and self._on_update is not None: 

813 self._on_update(self) 

814 

815 def discard(self, header: str) -> None: 

816 """Like :meth:`remove` but ignores errors. 

817 

818 :param header: the header to be discarded. 

819 """ 

820 try: 

821 self.remove(header) 

822 except KeyError: 

823 pass 

824 

825 def find(self, header: str) -> int: 

826 """Return the index of the header in the set or return -1 if not found. 

827 

828 :param header: the header to be looked up. 

829 """ 

830 header = header.lower() 

831 for idx, item in enumerate(self._headers): 

832 if item.lower() == header: 

833 return idx 

834 return -1 

835 

836 def index(self, header: str) -> int: 

837 """Return the index of the header in the set or raise an 

838 :exc:`IndexError`. 

839 

840 :param header: the header to be looked up. 

841 """ 

842 rv = self.find(header) 

843 if rv < 0: 

844 raise IndexError(header) 

845 return rv 

846 

847 def clear(self: te.Self) -> None: 

848 """Clear the set.""" 

849 self._set.clear() 

850 self._headers.clear() 

851 

852 if self._on_update is not None: 

853 self._on_update(self) 

854 

855 def as_set(self, preserve_casing: bool = False) -> set[str]: 

856 """Return the set as real python set type. When calling this, all 

857 the items are converted to lowercase and the ordering is lost. 

858 

859 :param preserve_casing: if set to `True` the items in the set returned 

860 will have the original case like in the 

861 :class:`HeaderSet`, otherwise they will 

862 be lowercase. 

863 """ 

864 if preserve_casing: 

865 return set(self._headers) 

866 return set(self._set) 

867 

868 @classmethod 

869 def from_header(cls, value: str | None) -> te.Self: 

870 """Parse a header value and create an instance of this class. 

871 

872 .. versionadded:: 3.2 

873 """ 

874 if not value: 

875 return cls() 

876 

877 return cls(parse_list_header(value)) 

878 

879 def to_header(self) -> str: 

880 """Convert to a header value.""" 

881 return dump_header(self._headers) 

882 

883 def __getitem__(self, idx: t.SupportsIndex) -> str: 

884 return self._headers[idx] 

885 

886 def __delitem__(self: te.Self, idx: t.SupportsIndex) -> None: 

887 rv = self._headers.pop(idx) 

888 self._set.remove(rv.lower()) 

889 if self._on_update is not None: 

890 self._on_update(self) 

891 

892 def __setitem__(self: te.Self, idx: t.SupportsIndex, value: str) -> None: 

893 old = self._headers[idx] 

894 self._set.remove(old.lower()) 

895 self._headers[idx] = value 

896 self._set.add(value.lower()) 

897 if self._on_update is not None: 

898 self._on_update(self) 

899 

900 def __contains__(self, header: str) -> bool: # type: ignore[override] 

901 return header.lower() in self._set 

902 

903 def __len__(self) -> int: 

904 return len(self._set) 

905 

906 def __iter__(self) -> cabc.Iterator[str]: 

907 return iter(self._headers) 

908 

909 def __bool__(self) -> bool: 

910 return bool(self._set) 

911 

912 def __str__(self) -> str: 

913 return self.to_header() 

914 

915 def __repr__(self) -> str: 

916 return f"{type(self).__name__}({self._headers!r})"