Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/boltons/dictutils.py: 29%

567 statements  

« prev     ^ index     » next       coverage.py v7.4.0, created at 2024-01-11 06:58 +0000

1# -*- coding: utf-8 -*- 

2 

3# Copyright (c) 2013, Mahmoud Hashemi 

4# 

5# Redistribution and use in source and binary forms, with or without 

6# modification, are permitted provided that the following conditions are 

7# met: 

8# 

9# * Redistributions of source code must retain the above copyright 

10# notice, this list of conditions and the following disclaimer. 

11# 

12# * Redistributions in binary form must reproduce the above 

13# copyright notice, this list of conditions and the following 

14# disclaimer in the documentation and/or other materials provided 

15# with the distribution. 

16# 

17# * The names of the contributors may not be used to endorse or 

18# promote products derived from this software without specific 

19# prior written permission. 

20# 

21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 

22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 

23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 

24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 

25# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 

26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 

27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 

28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 

29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 

30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 

31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

32 

33"""Python has a very powerful mapping type at its core: the :class:`dict` 

34type. While versatile and featureful, the :class:`dict` prioritizes 

35simplicity and performance. As a result, it does not retain the order 

36of item insertion [1]_, nor does it store multiple values per key. It 

37is a fast, unordered 1:1 mapping. 

38 

39The :class:`OrderedMultiDict` contrasts to the built-in :class:`dict`, 

40as a relatively maximalist, ordered 1:n subtype of 

41:class:`dict`. Virtually every feature of :class:`dict` has been 

42retooled to be intuitive in the face of this added 

43complexity. Additional methods have been added, such as 

44:class:`collections.Counter`-like functionality. 

45 

46A prime advantage of the :class:`OrderedMultiDict` (OMD) is its 

47non-destructive nature. Data can be added to an :class:`OMD` without being 

48rearranged or overwritten. The property can allow the developer to 

49work more freely with the data, as well as make more assumptions about 

50where input data will end up in the output, all without any extra 

51work. 

52 

53One great example of this is the :meth:`OMD.inverted()` method, which 

54returns a new OMD with the values as keys and the keys as values. All 

55the data and the respective order is still represented in the inverted 

56form, all from an operation which would be outright wrong and reckless 

57with a built-in :class:`dict` or :class:`collections.OrderedDict`. 

58 

59The OMD has been performance tuned to be suitable for a wide range of 

60usages, including as a basic unordered MultiDict. Special 

61thanks to `Mark Williams`_ for all his help. 

62 

63.. [1] As of 2015, `basic dicts on PyPy are ordered 

64 <http://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html>`_, 

65 and as of December 2017, `basic dicts in CPython 3 are now ordered 

66 <https://mail.python.org/pipermail/python-dev/2017-December/151283.html>`_, as 

67 well. 

68.. _Mark Williams: https://github.com/markrwilliams 

69 

70""" 

71 

72try: 

73 from collections.abc import KeysView, ValuesView, ItemsView 

74 PY3 = True 

75except ImportError: 

76 from collections import KeysView, ValuesView, ItemsView 

77 PY3 = False 

78 

79import itertools 

80 

81try: 

82 from itertools import izip_longest 

83except ImportError: 

84 from itertools import zip_longest as izip_longest 

85 

86try: 

87 from .typeutils import make_sentinel 

88 _MISSING = make_sentinel(var_name='_MISSING') 

89except ImportError: 

90 _MISSING = object() 

91 

92 

93PREV, NEXT, KEY, VALUE, SPREV, SNEXT = range(6) 

94 

95 

96__all__ = ['MultiDict', 'OMD', 'OrderedMultiDict', 'OneToOne', 'ManyToMany', 'subdict', 'FrozenDict'] 

97 

98try: 

99 profile 

100except NameError: 

101 profile = lambda x: x 

102 

103 

104class OrderedMultiDict(dict): 

105 """A MultiDict is a dictionary that can have multiple values per key 

106 and the OrderedMultiDict (OMD) is a MultiDict that retains 

107 original insertion order. Common use cases include: 

108 

109 * handling query strings parsed from URLs 

110 * inverting a dictionary to create a reverse index (values to keys) 

111 * stacking data from multiple dictionaries in a non-destructive way 

112 

113 The OrderedMultiDict constructor is identical to the built-in 

114 :class:`dict`, and overall the API constitutes an intuitive 

115 superset of the built-in type: 

116 

117 >>> omd = OrderedMultiDict() 

118 >>> omd['a'] = 1 

119 >>> omd['b'] = 2 

120 >>> omd.add('a', 3) 

121 >>> omd.get('a') 

122 3 

123 >>> omd.getlist('a') 

124 [1, 3] 

125 

126 Some non-:class:`dict`-like behaviors also make an appearance, 

127 such as support for :func:`reversed`: 

128 

129 >>> list(reversed(omd)) 

130 ['b', 'a'] 

131 

132 Note that unlike some other MultiDicts, this OMD gives precedence 

133 to the most recent value added. ``omd['a']`` refers to ``3``, not 

134 ``1``. 

135 

136 >>> omd 

137 OrderedMultiDict([('a', 1), ('b', 2), ('a', 3)]) 

138 >>> omd.poplast('a') 

139 3 

140 >>> omd 

141 OrderedMultiDict([('a', 1), ('b', 2)]) 

142 >>> omd.pop('a') 

143 1 

144 >>> omd 

145 OrderedMultiDict([('b', 2)]) 

146 

147 If you want a safe-to-modify or flat dictionary, use 

148 :meth:`OrderedMultiDict.todict()`. 

149 

150 >>> from pprint import pprint as pp # preserve printed ordering 

151 >>> omd = OrderedMultiDict([('a', 1), ('b', 2), ('a', 3)]) 

152 >>> pp(omd.todict()) 

153 {'a': 3, 'b': 2} 

154 >>> pp(omd.todict(multi=True)) 

155 {'a': [1, 3], 'b': [2]} 

156 

157 With ``multi=False``, items appear with the keys in to original 

158 insertion order, alongside the most-recently inserted value for 

159 that key. 

160 

161 >>> OrderedMultiDict([('a', 1), ('b', 2), ('a', 3)]).items(multi=False) 

162 [('a', 3), ('b', 2)] 

163 

164 .. warning:: 

165 

166 ``dict(omd)`` changed behavior `in Python 3.7 

167 <https://bugs.python.org/issue34320>`_ due to changes made to 

168 support the transition from :class:`collections.OrderedDict` to 

169 the built-in dictionary being ordered. Before 3.7, the result 

170 would be a new dictionary, with values that were lists, similar 

171 to ``omd.todict(multi=True)`` (but only shallow-copy; the lists 

172 were direct references to OMD internal structures). From 3.7 

173 onward, the values became singular, like 

174 ``omd.todict(multi=False)``. For reliable cross-version 

175 behavior, just use :meth:`~OrderedMultiDict.todict()`. 

176 

177 """ 

178 def __new__(cls, *a, **kw): 

179 ret = super(OrderedMultiDict, cls).__new__(cls) 

180 ret._clear_ll() 

181 return ret 

182 

183 def __init__(self, *args, **kwargs): 

184 if len(args) > 1: 

185 raise TypeError('%s expected at most 1 argument, got %s' 

186 % (self.__class__.__name__, len(args))) 

187 super(OrderedMultiDict, self).__init__() 

188 

189 if args: 

190 self.update_extend(args[0]) 

191 if kwargs: 

192 self.update(kwargs) 

193 

194 if PY3: 

195 def __getstate__(self): 

196 return list(self.iteritems(multi=True)) 

197 

198 def __setstate__(self, state): 

199 self.clear() 

200 self.update_extend(state) 

201 

202 def _clear_ll(self): 

203 try: 

204 _map = self._map 

205 except AttributeError: 

206 _map = self._map = {} 

207 self.root = [] 

208 _map.clear() 

209 self.root[:] = [self.root, self.root, None] 

210 

211 def _insert(self, k, v): 

212 root = self.root 

213 cells = self._map.setdefault(k, []) 

214 last = root[PREV] 

215 cell = [last, root, k, v] 

216 last[NEXT] = root[PREV] = cell 

217 cells.append(cell) 

218 

219 def add(self, k, v): 

220 """Add a single value *v* under a key *k*. Existing values under *k* 

221 are preserved. 

222 """ 

223 values = super(OrderedMultiDict, self).setdefault(k, []) 

224 self._insert(k, v) 

225 values.append(v) 

226 

227 def addlist(self, k, v): 

228 """Add an iterable of values underneath a specific key, preserving 

229 any values already under that key. 

230 

231 >>> omd = OrderedMultiDict([('a', -1)]) 

232 >>> omd.addlist('a', range(3)) 

233 >>> omd 

234 OrderedMultiDict([('a', -1), ('a', 0), ('a', 1), ('a', 2)]) 

235 

236 Called ``addlist`` for consistency with :meth:`getlist`, but 

237 tuples and other sequences and iterables work. 

238 """ 

239 if not v: 

240 return 

241 self_insert = self._insert 

242 values = super(OrderedMultiDict, self).setdefault(k, []) 

243 for subv in v: 

244 self_insert(k, subv) 

245 values.extend(v) 

246 

247 def get(self, k, default=None): 

248 """Return the value for key *k* if present in the dictionary, else 

249 *default*. If *default* is not given, ``None`` is returned. 

250 This method never raises a :exc:`KeyError`. 

251 

252 To get all values under a key, use :meth:`OrderedMultiDict.getlist`. 

253 """ 

254 return super(OrderedMultiDict, self).get(k, [default])[-1] 

255 

256 def getlist(self, k, default=_MISSING): 

257 """Get all values for key *k* as a list, if *k* is in the 

258 dictionary, else *default*. The list returned is a copy and 

259 can be safely mutated. If *default* is not given, an empty 

260 :class:`list` is returned. 

261 """ 

262 try: 

263 return super(OrderedMultiDict, self).__getitem__(k)[:] 

264 except KeyError: 

265 if default is _MISSING: 

266 return [] 

267 return default 

268 

269 def clear(self): 

270 "Empty the dictionary." 

271 super(OrderedMultiDict, self).clear() 

272 self._clear_ll() 

273 

274 def setdefault(self, k, default=_MISSING): 

275 """If key *k* is in the dictionary, return its value. If not, insert 

276 *k* with a value of *default* and return *default*. *default* 

277 defaults to ``None``. See :meth:`dict.setdefault` for more 

278 information. 

279 """ 

280 if not super(OrderedMultiDict, self).__contains__(k): 

281 self[k] = None if default is _MISSING else default 

282 return self[k] 

283 

284 def copy(self): 

285 "Return a shallow copy of the dictionary." 

286 return self.__class__(self.iteritems(multi=True)) 

287 

288 @classmethod 

289 def fromkeys(cls, keys, default=None): 

290 """Create a dictionary from a list of keys, with all the values 

291 set to *default*, or ``None`` if *default* is not set. 

292 """ 

293 return cls([(k, default) for k in keys]) 

294 

295 def update(self, E, **F): 

296 """Add items from a dictionary or iterable (and/or keyword arguments), 

297 overwriting values under an existing key. See 

298 :meth:`dict.update` for more details. 

299 """ 

300 # E and F are throwback names to the dict() __doc__ 

301 if E is self: 

302 return 

303 self_add = self.add 

304 if isinstance(E, OrderedMultiDict): 

305 for k in E: 

306 if k in self: 

307 del self[k] 

308 for k, v in E.iteritems(multi=True): 

309 self_add(k, v) 

310 elif callable(getattr(E, 'keys', None)): 

311 for k in E.keys(): 

312 self[k] = E[k] 

313 else: 

314 seen = set() 

315 seen_add = seen.add 

316 for k, v in E: 

317 if k not in seen and k in self: 

318 del self[k] 

319 seen_add(k) 

320 self_add(k, v) 

321 for k in F: 

322 self[k] = F[k] 

323 return 

324 

325 def update_extend(self, E, **F): 

326 """Add items from a dictionary, iterable, and/or keyword 

327 arguments without overwriting existing items present in the 

328 dictionary. Like :meth:`update`, but adds to existing keys 

329 instead of overwriting them. 

330 """ 

331 if E is self: 

332 iterator = iter(E.items()) 

333 elif isinstance(E, OrderedMultiDict): 

334 iterator = E.iteritems(multi=True) 

335 elif hasattr(E, 'keys'): 

336 iterator = ((k, E[k]) for k in E.keys()) 

337 else: 

338 iterator = E 

339 

340 self_add = self.add 

341 for k, v in iterator: 

342 self_add(k, v) 

343 

344 def __setitem__(self, k, v): 

345 if super(OrderedMultiDict, self).__contains__(k): 

346 self._remove_all(k) 

347 self._insert(k, v) 

348 super(OrderedMultiDict, self).__setitem__(k, [v]) 

349 

350 def __getitem__(self, k): 

351 return super(OrderedMultiDict, self).__getitem__(k)[-1] 

352 

353 def __delitem__(self, k): 

354 super(OrderedMultiDict, self).__delitem__(k) 

355 self._remove_all(k) 

356 

357 def __eq__(self, other): 

358 if self is other: 

359 return True 

360 try: 

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

362 return False 

363 except TypeError: 

364 return False 

365 if isinstance(other, OrderedMultiDict): 

366 selfi = self.iteritems(multi=True) 

367 otheri = other.iteritems(multi=True) 

368 zipped_items = izip_longest(selfi, otheri, fillvalue=(None, None)) 

369 for (selfk, selfv), (otherk, otherv) in zipped_items: 

370 if selfk != otherk or selfv != otherv: 

371 return False 

372 if not(next(selfi, _MISSING) is _MISSING 

373 and next(otheri, _MISSING) is _MISSING): 

374 # leftovers (TODO: watch for StopIteration?) 

375 return False 

376 return True 

377 elif hasattr(other, 'keys'): 

378 for selfk in self: 

379 try: 

380 other[selfk] == self[selfk] 

381 except KeyError: 

382 return False 

383 return True 

384 return False 

385 

386 def __ne__(self, other): 

387 return not (self == other) 

388 

389 def __ior__(self, other): 

390 self.update(other) 

391 return self 

392 

393 def pop(self, k, default=_MISSING): 

394 """Remove all values under key *k*, returning the most-recently 

395 inserted value. Raises :exc:`KeyError` if the key is not 

396 present and no *default* is provided. 

397 """ 

398 try: 

399 return self.popall(k)[-1] 

400 except KeyError: 

401 if default is _MISSING: 

402 raise KeyError(k) 

403 return default 

404 

405 def popall(self, k, default=_MISSING): 

406 """Remove all values under key *k*, returning them in the form of 

407 a list. Raises :exc:`KeyError` if the key is not present and no 

408 *default* is provided. 

409 """ 

410 super_self = super(OrderedMultiDict, self) 

411 if super_self.__contains__(k): 

412 self._remove_all(k) 

413 if default is _MISSING: 

414 return super_self.pop(k) 

415 return super_self.pop(k, default) 

416 

417 def poplast(self, k=_MISSING, default=_MISSING): 

418 """Remove and return the most-recently inserted value under the key 

419 *k*, or the most-recently inserted key if *k* is not 

420 provided. If no values remain under *k*, it will be removed 

421 from the OMD. Raises :exc:`KeyError` if *k* is not present in 

422 the dictionary, or the dictionary is empty. 

423 """ 

424 if k is _MISSING: 

425 if self: 

426 k = self.root[PREV][KEY] 

427 else: 

428 if default is _MISSING: 

429 raise KeyError('empty %r' % type(self)) 

430 return default 

431 try: 

432 self._remove(k) 

433 except KeyError: 

434 if default is _MISSING: 

435 raise KeyError(k) 

436 return default 

437 values = super(OrderedMultiDict, self).__getitem__(k) 

438 v = values.pop() 

439 if not values: 

440 super(OrderedMultiDict, self).__delitem__(k) 

441 return v 

442 

443 def _remove(self, k): 

444 values = self._map[k] 

445 cell = values.pop() 

446 cell[PREV][NEXT], cell[NEXT][PREV] = cell[NEXT], cell[PREV] 

447 if not values: 

448 del self._map[k] 

449 

450 def _remove_all(self, k): 

451 values = self._map[k] 

452 while values: 

453 cell = values.pop() 

454 cell[PREV][NEXT], cell[NEXT][PREV] = cell[NEXT], cell[PREV] 

455 del self._map[k] 

456 

457 def iteritems(self, multi=False): 

458 """Iterate over the OMD's items in insertion order. By default, 

459 yields only the most-recently inserted value for each key. Set 

460 *multi* to ``True`` to get all inserted items. 

461 """ 

462 root = self.root 

463 curr = root[NEXT] 

464 if multi: 

465 while curr is not root: 

466 yield curr[KEY], curr[VALUE] 

467 curr = curr[NEXT] 

468 else: 

469 for key in self.iterkeys(): 

470 yield key, self[key] 

471 

472 def iterkeys(self, multi=False): 

473 """Iterate over the OMD's keys in insertion order. By default, yields 

474 each key once, according to the most recent insertion. Set 

475 *multi* to ``True`` to get all keys, including duplicates, in 

476 insertion order. 

477 """ 

478 root = self.root 

479 curr = root[NEXT] 

480 if multi: 

481 while curr is not root: 

482 yield curr[KEY] 

483 curr = curr[NEXT] 

484 else: 

485 yielded = set() 

486 yielded_add = yielded.add 

487 while curr is not root: 

488 k = curr[KEY] 

489 if k not in yielded: 

490 yielded_add(k) 

491 yield k 

492 curr = curr[NEXT] 

493 

494 def itervalues(self, multi=False): 

495 """Iterate over the OMD's values in insertion order. By default, 

496 yields the most-recently inserted value per unique key. Set 

497 *multi* to ``True`` to get all values according to insertion 

498 order. 

499 """ 

500 for k, v in self.iteritems(multi=multi): 

501 yield v 

502 

503 def todict(self, multi=False): 

504 """Gets a basic :class:`dict` of the items in this dictionary. Keys 

505 are the same as the OMD, values are the most recently inserted 

506 values for each key. 

507 

508 Setting the *multi* arg to ``True`` is yields the same 

509 result as calling :class:`dict` on the OMD, except that all the 

510 value lists are copies that can be safely mutated. 

511 """ 

512 if multi: 

513 return dict([(k, self.getlist(k)) for k in self]) 

514 return dict([(k, self[k]) for k in self]) 

515 

516 def sorted(self, key=None, reverse=False): 

517 """Similar to the built-in :func:`sorted`, except this method returns 

518 a new :class:`OrderedMultiDict` sorted by the provided key 

519 function, optionally reversed. 

520 

521 Args: 

522 key (callable): A callable to determine the sort key of 

523 each element. The callable should expect an **item** 

524 (key-value pair tuple). 

525 reverse (bool): Set to ``True`` to reverse the ordering. 

526 

527 >>> omd = OrderedMultiDict(zip(range(3), range(3))) 

528 >>> omd.sorted(reverse=True) 

529 OrderedMultiDict([(2, 2), (1, 1), (0, 0)]) 

530 

531 Note that the key function receives an **item** (key-value 

532 tuple), so the recommended signature looks like: 

533 

534 >>> omd = OrderedMultiDict(zip('hello', 'world')) 

535 >>> omd.sorted(key=lambda i: i[1]) # i[0] is the key, i[1] is the val 

536 OrderedMultiDict([('o', 'd'), ('l', 'l'), ('e', 'o'), ('l', 'r'), ('h', 'w')]) 

537 """ 

538 cls = self.__class__ 

539 return cls(sorted(self.iteritems(multi=True), key=key, reverse=reverse)) 

540 

541 def sortedvalues(self, key=None, reverse=False): 

542 """Returns a copy of the :class:`OrderedMultiDict` with the same keys 

543 in the same order as the original OMD, but the values within 

544 each keyspace have been sorted according to *key* and 

545 *reverse*. 

546 

547 Args: 

548 key (callable): A single-argument callable to determine 

549 the sort key of each element. The callable should expect 

550 an **item** (key-value pair tuple). 

551 reverse (bool): Set to ``True`` to reverse the ordering. 

552 

553 >>> omd = OrderedMultiDict() 

554 >>> omd.addlist('even', [6, 2]) 

555 >>> omd.addlist('odd', [1, 5]) 

556 >>> omd.add('even', 4) 

557 >>> omd.add('odd', 3) 

558 >>> somd = omd.sortedvalues() 

559 >>> somd.getlist('even') 

560 [2, 4, 6] 

561 >>> somd.keys(multi=True) == omd.keys(multi=True) 

562 True 

563 >>> omd == somd 

564 False 

565 >>> somd 

566 OrderedMultiDict([('even', 2), ('even', 4), ('odd', 1), ('odd', 3), ('even', 6), ('odd', 5)]) 

567 

568 As demonstrated above, contents and key order are 

569 retained. Only value order changes. 

570 """ 

571 try: 

572 superself_iteritems = super(OrderedMultiDict, self).iteritems() 

573 except AttributeError: 

574 superself_iteritems = super(OrderedMultiDict, self).items() 

575 # (not reverse) because they pop off in reverse order for reinsertion 

576 sorted_val_map = dict([(k, sorted(v, key=key, reverse=(not reverse))) 

577 for k, v in superself_iteritems]) 

578 ret = self.__class__() 

579 for k in self.iterkeys(multi=True): 

580 ret.add(k, sorted_val_map[k].pop()) 

581 return ret 

582 

583 def inverted(self): 

584 """Returns a new :class:`OrderedMultiDict` with values and keys 

585 swapped, like creating dictionary transposition or reverse 

586 index. Insertion order is retained and all keys and values 

587 are represented in the output. 

588 

589 >>> omd = OMD([(0, 2), (1, 2)]) 

590 >>> omd.inverted().getlist(2) 

591 [0, 1] 

592 

593 Inverting twice yields a copy of the original: 

594 

595 >>> omd.inverted().inverted() 

596 OrderedMultiDict([(0, 2), (1, 2)]) 

597 """ 

598 return self.__class__((v, k) for k, v in self.iteritems(multi=True)) 

599 

600 def counts(self): 

601 """Returns a mapping from key to number of values inserted under that 

602 key. Like :py:class:`collections.Counter`, but returns a new 

603 :class:`OrderedMultiDict`. 

604 """ 

605 # Returns an OMD because Counter/OrderedDict may not be 

606 # available, and neither Counter nor dict maintain order. 

607 super_getitem = super(OrderedMultiDict, self).__getitem__ 

608 return self.__class__((k, len(super_getitem(k))) for k in self) 

609 

610 def keys(self, multi=False): 

611 """Returns a list containing the output of :meth:`iterkeys`. See 

612 that method's docs for more details. 

613 """ 

614 return list(self.iterkeys(multi=multi)) 

615 

616 def values(self, multi=False): 

617 """Returns a list containing the output of :meth:`itervalues`. See 

618 that method's docs for more details. 

619 """ 

620 return list(self.itervalues(multi=multi)) 

621 

622 def items(self, multi=False): 

623 """Returns a list containing the output of :meth:`iteritems`. See 

624 that method's docs for more details. 

625 """ 

626 return list(self.iteritems(multi=multi)) 

627 

628 def __iter__(self): 

629 return self.iterkeys() 

630 

631 def __reversed__(self): 

632 root = self.root 

633 curr = root[PREV] 

634 lengths = {} 

635 lengths_sd = lengths.setdefault 

636 get_values = super(OrderedMultiDict, self).__getitem__ 

637 while curr is not root: 

638 k = curr[KEY] 

639 vals = get_values(k) 

640 if lengths_sd(k, 1) == len(vals): 

641 yield k 

642 lengths[k] += 1 

643 curr = curr[PREV] 

644 

645 def __repr__(self): 

646 cn = self.__class__.__name__ 

647 kvs = ', '.join([repr((k, v)) for k, v in self.iteritems(multi=True)]) 

648 return '%s([%s])' % (cn, kvs) 

649 

650 def viewkeys(self): 

651 "OMD.viewkeys() -> a set-like object providing a view on OMD's keys" 

652 return KeysView(self) 

653 

654 def viewvalues(self): 

655 "OMD.viewvalues() -> an object providing a view on OMD's values" 

656 return ValuesView(self) 

657 

658 def viewitems(self): 

659 "OMD.viewitems() -> a set-like object providing a view on OMD's items" 

660 return ItemsView(self) 

661 

662 

663# A couple of convenient aliases 

664OMD = OrderedMultiDict 

665MultiDict = OrderedMultiDict 

666 

667 

668class FastIterOrderedMultiDict(OrderedMultiDict): 

669 """An OrderedMultiDict backed by a skip list. Iteration over keys 

670 is faster and uses constant memory but adding duplicate key-value 

671 pairs is slower. Brainchild of Mark Williams. 

672 """ 

673 def _clear_ll(self): 

674 # TODO: always reset objects? (i.e., no else block below) 

675 try: 

676 _map = self._map 

677 except AttributeError: 

678 _map = self._map = {} 

679 self.root = [] 

680 _map.clear() 

681 self.root[:] = [self.root, self.root, 

682 None, None, 

683 self.root, self.root] 

684 

685 def _insert(self, k, v): 

686 root = self.root 

687 empty = [] 

688 cells = self._map.setdefault(k, empty) 

689 last = root[PREV] 

690 

691 if cells is empty: 

692 cell = [last, root, 

693 k, v, 

694 last, root] 

695 # was the last one skipped? 

696 if last[SPREV][SNEXT] is root: 

697 last[SPREV][SNEXT] = cell 

698 last[NEXT] = last[SNEXT] = root[PREV] = root[SPREV] = cell 

699 cells.append(cell) 

700 else: 

701 # if the previous was skipped, go back to the cell that 

702 # skipped it 

703 sprev = last[SPREV] if (last[SPREV][SNEXT] is not last) else last 

704 cell = [last, root, 

705 k, v, 

706 sprev, root] 

707 # skip me 

708 last[SNEXT] = root 

709 last[NEXT] = root[PREV] = root[SPREV] = cell 

710 cells.append(cell) 

711 

712 def _remove(self, k): 

713 cells = self._map[k] 

714 cell = cells.pop() 

715 if not cells: 

716 del self._map[k] 

717 cell[PREV][SNEXT] = cell[SNEXT] 

718 

719 if cell[PREV][SPREV][SNEXT] is cell: 

720 cell[PREV][SPREV][SNEXT] = cell[NEXT] 

721 elif cell[SNEXT] is cell[NEXT]: 

722 cell[SPREV][SNEXT], cell[SNEXT][SPREV] = cell[SNEXT], cell[SPREV] 

723 

724 cell[PREV][NEXT], cell[NEXT][PREV] = cell[NEXT], cell[PREV] 

725 

726 def _remove_all(self, k): 

727 cells = self._map.pop(k) 

728 while cells: 

729 cell = cells.pop() 

730 if cell[PREV][SPREV][SNEXT] is cell: 

731 cell[PREV][SPREV][SNEXT] = cell[NEXT] 

732 elif cell[SNEXT] is cell[NEXT]: 

733 cell[SPREV][SNEXT], cell[SNEXT][SPREV] = cell[SNEXT], cell[SPREV] 

734 

735 cell[PREV][NEXT], cell[NEXT][PREV] = cell[NEXT], cell[PREV] 

736 cell[PREV][SNEXT] = cell[SNEXT] 

737 

738 def iteritems(self, multi=False): 

739 next_link = NEXT if multi else SNEXT 

740 root = self.root 

741 curr = root[next_link] 

742 while curr is not root: 

743 yield curr[KEY], curr[VALUE] 

744 curr = curr[next_link] 

745 

746 def iterkeys(self, multi=False): 

747 next_link = NEXT if multi else SNEXT 

748 root = self.root 

749 curr = root[next_link] 

750 while curr is not root: 

751 yield curr[KEY] 

752 curr = curr[next_link] 

753 

754 def __reversed__(self): 

755 root = self.root 

756 curr = root[PREV] 

757 while curr is not root: 

758 if curr[SPREV][SNEXT] is not curr: 

759 curr = curr[SPREV] 

760 if curr is root: 

761 break 

762 yield curr[KEY] 

763 curr = curr[PREV] 

764 

765 

766_OTO_INV_MARKER = object() 

767_OTO_UNIQUE_MARKER = object() 

768 

769 

770class OneToOne(dict): 

771 """Implements a one-to-one mapping dictionary. In addition to 

772 inheriting from and behaving exactly like the builtin 

773 :class:`dict`, all values are automatically added as keys on a 

774 reverse mapping, available as the `inv` attribute. This 

775 arrangement keeps key and value namespaces distinct. 

776 

777 Basic operations are intuitive: 

778 

779 >>> oto = OneToOne({'a': 1, 'b': 2}) 

780 >>> print(oto['a']) 

781 1 

782 >>> print(oto.inv[1]) 

783 a 

784 >>> len(oto) 

785 2 

786 

787 Overwrites happens in both directions: 

788 

789 >>> oto.inv[1] = 'c' 

790 >>> print(oto.get('a')) 

791 None 

792 >>> len(oto) 

793 2 

794 

795 For a very similar project, with even more one-to-one 

796 functionality, check out `bidict <https://github.com/jab/bidict>`_. 

797 """ 

798 __slots__ = ('inv',) 

799 

800 def __init__(self, *a, **kw): 

801 raise_on_dupe = False 

802 if a: 

803 if a[0] is _OTO_INV_MARKER: 

804 self.inv = a[1] 

805 dict.__init__(self, [(v, k) for k, v in self.inv.items()]) 

806 return 

807 elif a[0] is _OTO_UNIQUE_MARKER: 

808 a, raise_on_dupe = a[1:], True 

809 

810 dict.__init__(self, *a, **kw) 

811 self.inv = self.__class__(_OTO_INV_MARKER, self) 

812 

813 if len(self) == len(self.inv): 

814 # if lengths match, that means everything's unique 

815 return 

816 

817 if not raise_on_dupe: 

818 dict.clear(self) 

819 dict.update(self, [(v, k) for k, v in self.inv.items()]) 

820 return 

821 

822 # generate an error message if the values aren't 1:1 

823 

824 val_multidict = {} 

825 for k, v in self.items(): 

826 val_multidict.setdefault(v, []).append(k) 

827 

828 dupes = dict([(v, k_list) for v, k_list in 

829 val_multidict.items() if len(k_list) > 1]) 

830 

831 raise ValueError('expected unique values, got multiple keys for' 

832 ' the following values: %r' % dupes) 

833 

834 @classmethod 

835 def unique(cls, *a, **kw): 

836 """This alternate constructor for OneToOne will raise an exception 

837 when input values overlap. For instance: 

838 

839 >>> OneToOne.unique({'a': 1, 'b': 1}) 

840 Traceback (most recent call last): 

841 ... 

842 ValueError: expected unique values, got multiple keys for the following values: ... 

843 

844 This even works across inputs: 

845 

846 >>> a_dict = {'a': 2} 

847 >>> OneToOne.unique(a_dict, b=2) 

848 Traceback (most recent call last): 

849 ... 

850 ValueError: expected unique values, got multiple keys for the following values: ... 

851 """ 

852 return cls(_OTO_UNIQUE_MARKER, *a, **kw) 

853 

854 def __setitem__(self, key, val): 

855 hash(val) # ensure val is a valid key 

856 if key in self: 

857 dict.__delitem__(self.inv, self[key]) 

858 if val in self.inv: 

859 del self.inv[val] 

860 dict.__setitem__(self, key, val) 

861 dict.__setitem__(self.inv, val, key) 

862 

863 def __delitem__(self, key): 

864 dict.__delitem__(self.inv, self[key]) 

865 dict.__delitem__(self, key) 

866 

867 def clear(self): 

868 dict.clear(self) 

869 dict.clear(self.inv) 

870 

871 def copy(self): 

872 return self.__class__(self) 

873 

874 def pop(self, key, default=_MISSING): 

875 if key in self: 

876 dict.__delitem__(self.inv, self[key]) 

877 return dict.pop(self, key) 

878 if default is not _MISSING: 

879 return default 

880 raise KeyError() 

881 

882 def popitem(self): 

883 key, val = dict.popitem(self) 

884 dict.__delitem__(self.inv, val) 

885 return key, val 

886 

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

888 if key not in self: 

889 self[key] = default 

890 return self[key] 

891 

892 def update(self, dict_or_iterable, **kw): 

893 if isinstance(dict_or_iterable, dict): 

894 for val in dict_or_iterable.values(): 

895 hash(val) 

896 keys_vals = list(dict_or_iterable.items()) 

897 else: 

898 for key, val in dict_or_iterable: 

899 hash(key) 

900 hash(val) 

901 keys_vals = list(dict_or_iterable) 

902 for val in kw.values(): 

903 hash(val) 

904 keys_vals.extend(kw.items()) 

905 for key, val in keys_vals: 

906 self[key] = val 

907 

908 def __repr__(self): 

909 cn = self.__class__.__name__ 

910 dict_repr = dict.__repr__(self) 

911 return "%s(%s)" % (cn, dict_repr) 

912 

913 

914# marker for the secret handshake used internally to set up the invert ManyToMany 

915_PAIRING = object() 

916 

917 

918class ManyToMany(object): 

919 """ 

920 a dict-like entity that represents a many-to-many relationship 

921 between two groups of objects 

922 

923 behaves like a dict-of-tuples; also has .inv which is kept 

924 up to date which is a dict-of-tuples in the other direction 

925 

926 also, can be used as a directed graph among hashable python objects 

927 """ 

928 def __init__(self, items=None): 

929 self.data = {} 

930 if type(items) is tuple and items and items[0] is _PAIRING: 

931 self.inv = items[1] 

932 else: 

933 self.inv = self.__class__((_PAIRING, self)) 

934 if items: 

935 self.update(items) 

936 return 

937 

938 def get(self, key, default=frozenset()): 

939 try: 

940 return self[key] 

941 except KeyError: 

942 return default 

943 

944 def __getitem__(self, key): 

945 return frozenset(self.data[key]) 

946 

947 def __setitem__(self, key, vals): 

948 vals = set(vals) 

949 if key in self: 

950 to_remove = self.data[key] - vals 

951 vals -= self.data[key] 

952 for val in to_remove: 

953 self.remove(key, val) 

954 for val in vals: 

955 self.add(key, val) 

956 

957 def __delitem__(self, key): 

958 for val in self.data.pop(key): 

959 self.inv.data[val].remove(key) 

960 if not self.inv.data[val]: 

961 del self.inv.data[val] 

962 

963 def update(self, iterable): 

964 """given an iterable of (key, val), add them all""" 

965 if type(iterable) is type(self): 

966 other = iterable 

967 for k in other.data: 

968 if k not in self.data: 

969 self.data[k] = other.data[k] 

970 else: 

971 self.data[k].update(other.data[k]) 

972 for k in other.inv.data: 

973 if k not in self.inv.data: 

974 self.inv.data[k] = other.inv.data[k] 

975 else: 

976 self.inv.data[k].update(other.inv.data[k]) 

977 elif callable(getattr(iterable, 'keys', None)): 

978 for k in iterable.keys(): 

979 self.add(k, iterable[k]) 

980 else: 

981 for key, val in iterable: 

982 self.add(key, val) 

983 return 

984 

985 def add(self, key, val): 

986 if key not in self.data: 

987 self.data[key] = set() 

988 self.data[key].add(val) 

989 if val not in self.inv.data: 

990 self.inv.data[val] = set() 

991 self.inv.data[val].add(key) 

992 

993 def remove(self, key, val): 

994 self.data[key].remove(val) 

995 if not self.data[key]: 

996 del self.data[key] 

997 self.inv.data[val].remove(key) 

998 if not self.inv.data[val]: 

999 del self.inv.data[val] 

1000 

1001 def replace(self, key, newkey): 

1002 """ 

1003 replace instances of key by newkey 

1004 """ 

1005 if key not in self.data: 

1006 return 

1007 self.data[newkey] = fwdset = self.data.pop(key) 

1008 for val in fwdset: 

1009 revset = self.inv.data[val] 

1010 revset.remove(key) 

1011 revset.add(newkey) 

1012 

1013 def iteritems(self): 

1014 for key in self.data: 

1015 for val in self.data[key]: 

1016 yield key, val 

1017 

1018 def keys(self): 

1019 return self.data.keys() 

1020 

1021 def __contains__(self, key): 

1022 return key in self.data 

1023 

1024 def __iter__(self): 

1025 return self.data.__iter__() 

1026 

1027 def __len__(self): 

1028 return self.data.__len__() 

1029 

1030 def __eq__(self, other): 

1031 return type(self) == type(other) and self.data == other.data 

1032 

1033 def __repr__(self): 

1034 cn = self.__class__.__name__ 

1035 return '%s(%r)' % (cn, list(self.iteritems())) 

1036 

1037 

1038def subdict(d, keep=None, drop=None): 

1039 """Compute the "subdictionary" of a dict, *d*. 

1040 

1041 A subdict is to a dict what a subset is a to set. If *A* is a 

1042 subdict of *B*, that means that all keys of *A* are present in 

1043 *B*. 

1044 

1045 Returns a new dict with any keys in *drop* removed, and any keys 

1046 in *keep* still present, provided they were in the original 

1047 dict. *keep* defaults to all keys, *drop* defaults to empty, so 

1048 without one of these arguments, calling this function is 

1049 equivalent to calling ``dict()``. 

1050 

1051 >>> from pprint import pprint as pp 

1052 >>> pp(subdict({'a': 1, 'b': 2})) 

1053 {'a': 1, 'b': 2} 

1054 >>> subdict({'a': 1, 'b': 2, 'c': 3}, drop=['b', 'c']) 

1055 {'a': 1} 

1056 >>> pp(subdict({'a': 1, 'b': 2, 'c': 3}, keep=['a', 'c'])) 

1057 {'a': 1, 'c': 3} 

1058 

1059 """ 

1060 if keep is None: 

1061 keep = d.keys() 

1062 if drop is None: 

1063 drop = [] 

1064 

1065 keys = set(keep) - set(drop) 

1066 

1067 return type(d)([(k, v) for k, v in d.items() if k in keys]) 

1068 

1069 

1070class FrozenHashError(TypeError): 

1071 pass 

1072 

1073 

1074class FrozenDict(dict): 

1075 """An immutable dict subtype that is hashable and can itself be used 

1076 as a :class:`dict` key or :class:`set` entry. What 

1077 :class:`frozenset` is to :class:`set`, FrozenDict is to 

1078 :class:`dict`. 

1079 

1080 There was once an attempt to introduce such a type to the standard 

1081 library, but it was rejected: `PEP 416 <https://www.python.org/dev/peps/pep-0416/>`_. 

1082 

1083 Because FrozenDict is a :class:`dict` subtype, it automatically 

1084 works everywhere a dict would, including JSON serialization. 

1085 

1086 """ 

1087 __slots__ = ('_hash',) 

1088 

1089 def updated(self, *a, **kw): 

1090 """Make a copy and add items from a dictionary or iterable (and/or 

1091 keyword arguments), overwriting values under an existing 

1092 key. See :meth:`dict.update` for more details. 

1093 """ 

1094 data = dict(self) 

1095 data.update(*a, **kw) 

1096 return type(self)(data) 

1097 

1098 @classmethod 

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

1100 # one of the lesser known and used/useful dict methods 

1101 return cls(dict.fromkeys(keys, value)) 

1102 

1103 def __repr__(self): 

1104 cn = self.__class__.__name__ 

1105 return '%s(%s)' % (cn, dict.__repr__(self)) 

1106 

1107 def __reduce_ex__(self, protocol): 

1108 return type(self), (dict(self),) 

1109 

1110 def __hash__(self): 

1111 try: 

1112 ret = self._hash 

1113 except AttributeError: 

1114 try: 

1115 ret = self._hash = hash(frozenset(self.items())) 

1116 except Exception as e: 

1117 ret = self._hash = FrozenHashError(e) 

1118 

1119 if ret.__class__ is FrozenHashError: 

1120 raise ret 

1121 

1122 return ret 

1123 

1124 def __copy__(self): 

1125 return self # immutable types don't copy, see tuple's behavior 

1126 

1127 # block everything else 

1128 def _raise_frozen_typeerror(self, *a, **kw): 

1129 "raises a TypeError, because FrozenDicts are immutable" 

1130 raise TypeError('%s object is immutable' % self.__class__.__name__) 

1131 

1132 __ior__ = __setitem__ = __delitem__ = update = _raise_frozen_typeerror 

1133 setdefault = pop = popitem = clear = _raise_frozen_typeerror 

1134 

1135 del _raise_frozen_typeerror 

1136 

1137 

1138# end dictutils.py