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

491 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-09 07:17 +0000

1from __future__ import annotations 

2 

3from collections.abc import MutableSet 

4from copy import deepcopy 

5 

6from .. import exceptions 

7from .._internal import _missing 

8from .mixins import ImmutableDictMixin 

9from .mixins import ImmutableListMixin 

10from .mixins import ImmutableMultiDictMixin 

11from .mixins import UpdateDictMixin 

12 

13 

14def is_immutable(self): 

15 raise TypeError(f"{type(self).__name__!r} objects are immutable") 

16 

17 

18def iter_multi_items(mapping): 

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

20 without dropping any from more complex structures. 

21 """ 

22 if isinstance(mapping, MultiDict): 

23 yield from mapping.items(multi=True) 

24 elif isinstance(mapping, dict): 

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

26 if isinstance(value, (tuple, list)): 

27 for v in value: 

28 yield key, v 

29 else: 

30 yield key, value 

31 else: 

32 yield from mapping 

33 

34 

35class ImmutableList(ImmutableListMixin, list): 

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

37 

38 .. versionadded:: 0.5 

39 

40 :private: 

41 """ 

42 

43 def __repr__(self): 

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

45 

46 

47class TypeConversionDict(dict): 

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

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

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

51 

52 .. versionadded:: 0.5 

53 """ 

54 

55 def get(self, key, default=None, type=None): 

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

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

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

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

60 found: 

61 

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

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

64 42 

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

66 -1 

67 

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

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

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

71 returned. 

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

73 :class:`MultiDict`. If a :exc:`ValueError` is raised 

74 by this callable the default value is returned. 

75 """ 

76 try: 

77 rv = self[key] 

78 except KeyError: 

79 return default 

80 if type is not None: 

81 try: 

82 rv = type(rv) 

83 except ValueError: 

84 rv = default 

85 return rv 

86 

87 

88class ImmutableTypeConversionDict(ImmutableDictMixin, TypeConversionDict): 

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

90 modifications. 

91 

92 .. versionadded:: 0.5 

93 """ 

94 

95 def copy(self): 

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

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

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

99 """ 

100 return TypeConversionDict(self) 

101 

102 def __copy__(self): 

103 return self 

104 

105 

106class MultiDict(TypeConversionDict): 

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

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

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

110 elements pass multiple values for the same key. 

111 

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

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

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

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

116 explained below. 

117 

118 Basic Usage: 

119 

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

121 >>> d 

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

123 >>> d['a'] 

124 'b' 

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

126 ['b', 'c'] 

127 >>> 'a' in d 

128 True 

129 

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

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

132 

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

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

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

136 exceptions. 

137 

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

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

140 onwards some keyword parameters. 

141 

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

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

144 or `None`. 

145 """ 

146 

147 def __init__(self, mapping=None): 

148 if isinstance(mapping, MultiDict): 

149 dict.__init__(self, ((k, l[:]) for k, l in mapping.lists())) 

150 elif isinstance(mapping, dict): 

151 tmp = {} 

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

153 if isinstance(value, (tuple, list)): 

154 if len(value) == 0: 

155 continue 

156 value = list(value) 

157 else: 

158 value = [value] 

159 tmp[key] = value 

160 dict.__init__(self, tmp) 

161 else: 

162 tmp = {} 

163 for key, value in mapping or (): 

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

165 dict.__init__(self, tmp) 

166 

167 def __getstate__(self): 

168 return dict(self.lists()) 

169 

170 def __setstate__(self, value): 

171 dict.clear(self) 

172 dict.update(self, value) 

173 

174 def __iter__(self): 

175 # Work around https://bugs.python.org/issue43246. 

176 # (`return super().__iter__()` also works here, which makes this look 

177 # even more like it should be a no-op, yet it isn't.) 

178 return dict.__iter__(self) 

179 

180 def __getitem__(self, key): 

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

182 raises KeyError if not found. 

183 

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

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

186 """ 

187 

188 if key in self: 

189 lst = dict.__getitem__(self, key) 

190 if len(lst) > 0: 

191 return lst[0] 

192 raise exceptions.BadRequestKeyError(key) 

193 

194 def __setitem__(self, key, value): 

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

196 

197 :param key: the key for the value. 

198 :param value: the value to set. 

199 """ 

200 dict.__setitem__(self, key, [value]) 

201 

202 def add(self, key, value): 

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

204 

205 .. versionadded:: 0.6 

206 

207 :param key: the key for the value. 

208 :param value: the value to add. 

209 """ 

210 dict.setdefault(self, key, []).append(value) 

211 

212 def getlist(self, key, type=None): 

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

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

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

216 with the callable defined there. 

217 

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

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

220 :class:`MultiDict`. If a :exc:`ValueError` is raised 

221 by this callable the value will be removed from the list. 

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

223 """ 

224 try: 

225 rv = dict.__getitem__(self, key) 

226 except KeyError: 

227 return [] 

228 if type is None: 

229 return list(rv) 

230 result = [] 

231 for item in rv: 

232 try: 

233 result.append(type(item)) 

234 except ValueError: 

235 pass 

236 return result 

237 

238 def setlist(self, key, new_list): 

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

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

241 the dictionary. 

242 

243 >>> d = MultiDict() 

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

245 >>> d['foo'] 

246 '1' 

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

248 ['1', '2'] 

249 

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

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

252 are removed first. 

253 """ 

254 dict.__setitem__(self, key, list(new_list)) 

255 

256 def setdefault(self, key, default=None): 

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

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

259 

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

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

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

263 """ 

264 if key not in self: 

265 self[key] = default 

266 else: 

267 default = self[key] 

268 return default 

269 

270 def setlistdefault(self, key, default_list=None): 

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

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

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

274 to the list: 

275 

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

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

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

279 [1, 2, 3] 

280 

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

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

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

284 before returned. 

285 :return: a :class:`list` 

286 """ 

287 if key not in self: 

288 default_list = list(default_list or ()) 

289 dict.__setitem__(self, key, default_list) 

290 else: 

291 default_list = dict.__getitem__(self, key) 

292 return default_list 

293 

294 def items(self, multi=False): 

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

296 

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

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

299 contain pairs for the first value of each key. 

300 """ 

301 for key, values in dict.items(self): 

302 if multi: 

303 for value in values: 

304 yield key, value 

305 else: 

306 yield key, values[0] 

307 

308 def lists(self): 

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

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

311 for key, values in dict.items(self): 

312 yield key, list(values) 

313 

314 def values(self): 

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

316 for values in dict.values(self): 

317 yield values[0] 

318 

319 def listvalues(self): 

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

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

322 

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

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

325 True 

326 """ 

327 return dict.values(self) 

328 

329 def copy(self): 

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

331 return self.__class__(self) 

332 

333 def deepcopy(self, memo=None): 

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

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

336 

337 def to_dict(self, flat=True): 

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

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

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

341 

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

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

344 contain the first value for each key. 

345 :return: a :class:`dict` 

346 """ 

347 if flat: 

348 return dict(self.items()) 

349 return dict(self.lists()) 

350 

351 def update(self, mapping): 

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

353 

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

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

356 >>> a.update(b) 

357 >>> a 

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

359 

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

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

362 

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

364 >>> y = MultiDict() 

365 >>> y.update(x) 

366 >>> y 

367 MultiDict([]) 

368 """ 

369 for key, value in iter_multi_items(mapping): 

370 MultiDict.add(self, key, value) 

371 

372 def pop(self, key, default=_missing): 

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

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

375 

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

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

378 1 

379 >>> "foo" in d 

380 False 

381 

382 :param key: the key to pop. 

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

384 not in the dictionary. 

385 """ 

386 try: 

387 lst = dict.pop(self, key) 

388 

389 if len(lst) == 0: 

390 raise exceptions.BadRequestKeyError(key) 

391 

392 return lst[0] 

393 except KeyError: 

394 if default is not _missing: 

395 return default 

396 

397 raise exceptions.BadRequestKeyError(key) from None 

398 

399 def popitem(self): 

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

401 try: 

402 item = dict.popitem(self) 

403 

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

405 raise exceptions.BadRequestKeyError(item[0]) 

406 

407 return (item[0], item[1][0]) 

408 except KeyError as e: 

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

410 

411 def poplist(self, key): 

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

413 an empty list is returned. 

414 

415 .. versionchanged:: 0.5 

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

417 raising an error. 

418 """ 

419 return dict.pop(self, key, []) 

420 

421 def popitemlist(self): 

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

423 try: 

424 return dict.popitem(self) 

425 except KeyError as e: 

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

427 

428 def __copy__(self): 

429 return self.copy() 

430 

431 def __deepcopy__(self, memo): 

432 return self.deepcopy(memo=memo) 

433 

434 def __repr__(self): 

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

436 

437 

438class _omd_bucket: 

439 """Wraps values in the :class:`OrderedMultiDict`. This makes it 

440 possible to keep an order over multiple different keys. It requires 

441 a lot of extra memory and slows down access a lot, but makes it 

442 possible to access elements in O(1) and iterate in O(n). 

443 """ 

444 

445 __slots__ = ("prev", "key", "value", "next") 

446 

447 def __init__(self, omd, key, value): 

448 self.prev = omd._last_bucket 

449 self.key = key 

450 self.value = value 

451 self.next = None 

452 

453 if omd._first_bucket is None: 

454 omd._first_bucket = self 

455 if omd._last_bucket is not None: 

456 omd._last_bucket.next = self 

457 omd._last_bucket = self 

458 

459 def unlink(self, omd): 

460 if self.prev: 

461 self.prev.next = self.next 

462 if self.next: 

463 self.next.prev = self.prev 

464 if omd._first_bucket is self: 

465 omd._first_bucket = self.next 

466 if omd._last_bucket is self: 

467 omd._last_bucket = self.prev 

468 

469 

470class OrderedMultiDict(MultiDict): 

471 """Works like a regular :class:`MultiDict` but preserves the 

472 order of the fields. To convert the ordered multi dict into a 

473 list you can use the :meth:`items` method and pass it ``multi=True``. 

474 

475 In general an :class:`OrderedMultiDict` is an order of magnitude 

476 slower than a :class:`MultiDict`. 

477 

478 .. admonition:: note 

479 

480 Due to a limitation in Python you cannot convert an ordered 

481 multi dict into a regular dict by using ``dict(multidict)``. 

482 Instead you have to use the :meth:`to_dict` method, otherwise 

483 the internal bucket objects are exposed. 

484 """ 

485 

486 def __init__(self, mapping=None): 

487 dict.__init__(self) 

488 self._first_bucket = self._last_bucket = None 

489 if mapping is not None: 

490 OrderedMultiDict.update(self, mapping) 

491 

492 def __eq__(self, other): 

493 if not isinstance(other, MultiDict): 

494 return NotImplemented 

495 if isinstance(other, OrderedMultiDict): 

496 iter1 = iter(self.items(multi=True)) 

497 iter2 = iter(other.items(multi=True)) 

498 try: 

499 for k1, v1 in iter1: 

500 k2, v2 = next(iter2) 

501 if k1 != k2 or v1 != v2: 

502 return False 

503 except StopIteration: 

504 return False 

505 try: 

506 next(iter2) 

507 except StopIteration: 

508 return True 

509 return False 

510 if len(self) != len(other): 

511 return False 

512 for key, values in self.lists(): 

513 if other.getlist(key) != values: 

514 return False 

515 return True 

516 

517 __hash__ = None 

518 

519 def __reduce_ex__(self, protocol): 

520 return type(self), (list(self.items(multi=True)),) 

521 

522 def __getstate__(self): 

523 return list(self.items(multi=True)) 

524 

525 def __setstate__(self, values): 

526 dict.clear(self) 

527 for key, value in values: 

528 self.add(key, value) 

529 

530 def __getitem__(self, key): 

531 if key in self: 

532 return dict.__getitem__(self, key)[0].value 

533 raise exceptions.BadRequestKeyError(key) 

534 

535 def __setitem__(self, key, value): 

536 self.poplist(key) 

537 self.add(key, value) 

538 

539 def __delitem__(self, key): 

540 self.pop(key) 

541 

542 def keys(self): 

543 return (key for key, value in self.items()) 

544 

545 def __iter__(self): 

546 return iter(self.keys()) 

547 

548 def values(self): 

549 return (value for key, value in self.items()) 

550 

551 def items(self, multi=False): 

552 ptr = self._first_bucket 

553 if multi: 

554 while ptr is not None: 

555 yield ptr.key, ptr.value 

556 ptr = ptr.next 

557 else: 

558 returned_keys = set() 

559 while ptr is not None: 

560 if ptr.key not in returned_keys: 

561 returned_keys.add(ptr.key) 

562 yield ptr.key, ptr.value 

563 ptr = ptr.next 

564 

565 def lists(self): 

566 returned_keys = set() 

567 ptr = self._first_bucket 

568 while ptr is not None: 

569 if ptr.key not in returned_keys: 

570 yield ptr.key, self.getlist(ptr.key) 

571 returned_keys.add(ptr.key) 

572 ptr = ptr.next 

573 

574 def listvalues(self): 

575 for _key, values in self.lists(): 

576 yield values 

577 

578 def add(self, key, value): 

579 dict.setdefault(self, key, []).append(_omd_bucket(self, key, value)) 

580 

581 def getlist(self, key, type=None): 

582 try: 

583 rv = dict.__getitem__(self, key) 

584 except KeyError: 

585 return [] 

586 if type is None: 

587 return [x.value for x in rv] 

588 result = [] 

589 for item in rv: 

590 try: 

591 result.append(type(item.value)) 

592 except ValueError: 

593 pass 

594 return result 

595 

596 def setlist(self, key, new_list): 

597 self.poplist(key) 

598 for value in new_list: 

599 self.add(key, value) 

600 

601 def setlistdefault(self, key, default_list=None): 

602 raise TypeError("setlistdefault is unsupported for ordered multi dicts") 

603 

604 def update(self, mapping): 

605 for key, value in iter_multi_items(mapping): 

606 OrderedMultiDict.add(self, key, value) 

607 

608 def poplist(self, key): 

609 buckets = dict.pop(self, key, ()) 

610 for bucket in buckets: 

611 bucket.unlink(self) 

612 return [x.value for x in buckets] 

613 

614 def pop(self, key, default=_missing): 

615 try: 

616 buckets = dict.pop(self, key) 

617 except KeyError: 

618 if default is not _missing: 

619 return default 

620 

621 raise exceptions.BadRequestKeyError(key) from None 

622 

623 for bucket in buckets: 

624 bucket.unlink(self) 

625 

626 return buckets[0].value 

627 

628 def popitem(self): 

629 try: 

630 key, buckets = dict.popitem(self) 

631 except KeyError as e: 

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

633 

634 for bucket in buckets: 

635 bucket.unlink(self) 

636 

637 return key, buckets[0].value 

638 

639 def popitemlist(self): 

640 try: 

641 key, buckets = dict.popitem(self) 

642 except KeyError as e: 

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

644 

645 for bucket in buckets: 

646 bucket.unlink(self) 

647 

648 return key, [x.value for x in buckets] 

649 

650 

651class CombinedMultiDict(ImmutableMultiDictMixin, MultiDict): 

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

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

654 dicts: 

655 

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

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

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

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

660 >>> combined['foo'] 

661 'bar' 

662 >>> combined['blub'] 

663 'blah' 

664 

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

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

667 

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

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

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

671 exceptions. 

672 """ 

673 

674 def __reduce_ex__(self, protocol): 

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

676 

677 def __init__(self, dicts=None): 

678 self.dicts = list(dicts) or [] 

679 

680 @classmethod 

681 def fromkeys(cls, keys, value=None): 

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

683 

684 def __getitem__(self, key): 

685 for d in self.dicts: 

686 if key in d: 

687 return d[key] 

688 raise exceptions.BadRequestKeyError(key) 

689 

690 def get(self, key, default=None, type=None): 

691 for d in self.dicts: 

692 if key in d: 

693 if type is not None: 

694 try: 

695 return type(d[key]) 

696 except ValueError: 

697 continue 

698 return d[key] 

699 return default 

700 

701 def getlist(self, key, type=None): 

702 rv = [] 

703 for d in self.dicts: 

704 rv.extend(d.getlist(key, type)) 

705 return rv 

706 

707 def _keys_impl(self): 

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

709 saving one list creation from an iterator. 

710 """ 

711 rv = set() 

712 rv.update(*self.dicts) 

713 return rv 

714 

715 def keys(self): 

716 return self._keys_impl() 

717 

718 def __iter__(self): 

719 return iter(self.keys()) 

720 

721 def items(self, multi=False): 

722 found = set() 

723 for d in self.dicts: 

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

725 if multi: 

726 yield key, value 

727 elif key not in found: 

728 found.add(key) 

729 yield key, value 

730 

731 def values(self): 

732 for _key, value in self.items(): 

733 yield value 

734 

735 def lists(self): 

736 rv = {} 

737 for d in self.dicts: 

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

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

740 return list(rv.items()) 

741 

742 def listvalues(self): 

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

744 

745 def copy(self): 

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

747 

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

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

750 wrapped dicts. 

751 

752 .. versionchanged:: 0.15 

753 Return a mutable :class:`MultiDict`. 

754 """ 

755 return MultiDict(self) 

756 

757 def to_dict(self, flat=True): 

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

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

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

761 

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

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

764 contain the first item for each key. 

765 :return: a :class:`dict` 

766 """ 

767 if flat: 

768 return dict(self.items()) 

769 

770 return dict(self.lists()) 

771 

772 def __len__(self): 

773 return len(self._keys_impl()) 

774 

775 def __contains__(self, key): 

776 for d in self.dicts: 

777 if key in d: 

778 return True 

779 return False 

780 

781 def __repr__(self): 

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

783 

784 

785class ImmutableDict(ImmutableDictMixin, dict): 

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

787 

788 .. versionadded:: 0.5 

789 """ 

790 

791 def __repr__(self): 

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

793 

794 def copy(self): 

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

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

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

798 """ 

799 return dict(self) 

800 

801 def __copy__(self): 

802 return self 

803 

804 

805class ImmutableMultiDict(ImmutableMultiDictMixin, MultiDict): 

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

807 

808 .. versionadded:: 0.5 

809 """ 

810 

811 def copy(self): 

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

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

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

815 """ 

816 return MultiDict(self) 

817 

818 def __copy__(self): 

819 return self 

820 

821 

822class ImmutableOrderedMultiDict(ImmutableMultiDictMixin, OrderedMultiDict): 

823 """An immutable :class:`OrderedMultiDict`. 

824 

825 .. versionadded:: 0.6 

826 """ 

827 

828 def _iter_hashitems(self): 

829 return enumerate(self.items(multi=True)) 

830 

831 def copy(self): 

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

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

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

835 """ 

836 return OrderedMultiDict(self) 

837 

838 def __copy__(self): 

839 return self 

840 

841 

842class CallbackDict(UpdateDictMixin, dict): 

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

844 The function is passed the dict instance. 

845 """ 

846 

847 def __init__(self, initial=None, on_update=None): 

848 dict.__init__(self, initial or ()) 

849 self.on_update = on_update 

850 

851 def __repr__(self): 

852 return f"<{type(self).__name__} {dict.__repr__(self)}>" 

853 

854 

855class HeaderSet(MutableSet): 

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

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

858 content-language headers. 

859 

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

861 instantiation works like this: 

862 

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

864 >>> hs 

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

866 """ 

867 

868 def __init__(self, headers=None, on_update=None): 

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

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

871 self.on_update = on_update 

872 

873 def add(self, header): 

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

875 self.update((header,)) 

876 

877 def remove(self, header): 

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

879 header is not in the set. 

880 

881 .. versionchanged:: 0.5 

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

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

884 

885 :param header: the header to be removed. 

886 """ 

887 key = header.lower() 

888 if key not in self._set: 

889 raise KeyError(header) 

890 self._set.remove(key) 

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

892 if key.lower() == header: 

893 del self._headers[idx] 

894 break 

895 if self.on_update is not None: 

896 self.on_update(self) 

897 

898 def update(self, iterable): 

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

900 

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

902 """ 

903 inserted_any = False 

904 for header in iterable: 

905 key = header.lower() 

906 if key not in self._set: 

907 self._headers.append(header) 

908 self._set.add(key) 

909 inserted_any = True 

910 if inserted_any and self.on_update is not None: 

911 self.on_update(self) 

912 

913 def discard(self, header): 

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

915 

916 :param header: the header to be discarded. 

917 """ 

918 try: 

919 self.remove(header) 

920 except KeyError: 

921 pass 

922 

923 def find(self, header): 

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

925 

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

927 """ 

928 header = header.lower() 

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

930 if item.lower() == header: 

931 return idx 

932 return -1 

933 

934 def index(self, header): 

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

936 :exc:`IndexError`. 

937 

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

939 """ 

940 rv = self.find(header) 

941 if rv < 0: 

942 raise IndexError(header) 

943 return rv 

944 

945 def clear(self): 

946 """Clear the set.""" 

947 self._set.clear() 

948 del self._headers[:] 

949 if self.on_update is not None: 

950 self.on_update(self) 

951 

952 def as_set(self, preserve_casing=False): 

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

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

955 

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

957 will have the original case like in the 

958 :class:`HeaderSet`, otherwise they will 

959 be lowercase. 

960 """ 

961 if preserve_casing: 

962 return set(self._headers) 

963 return set(self._set) 

964 

965 def to_header(self): 

966 """Convert the header set into an HTTP header string.""" 

967 return ", ".join(map(http.quote_header_value, self._headers)) 

968 

969 def __getitem__(self, idx): 

970 return self._headers[idx] 

971 

972 def __delitem__(self, idx): 

973 rv = self._headers.pop(idx) 

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

975 if self.on_update is not None: 

976 self.on_update(self) 

977 

978 def __setitem__(self, idx, value): 

979 old = self._headers[idx] 

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

981 self._headers[idx] = value 

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

983 if self.on_update is not None: 

984 self.on_update(self) 

985 

986 def __contains__(self, header): 

987 return header.lower() in self._set 

988 

989 def __len__(self): 

990 return len(self._set) 

991 

992 def __iter__(self): 

993 return iter(self._headers) 

994 

995 def __bool__(self): 

996 return bool(self._set) 

997 

998 def __str__(self): 

999 return self.to_header() 

1000 

1001 def __repr__(self): 

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

1003 

1004 

1005# circular dependencies 

1006from .. import http