Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/SQLAlchemy-1.3.25.dev0-py3.11-linux-x86_64.egg/sqlalchemy/util/_collections.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

632 statements  

1# util/_collections.py 

2# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: http://www.opensource.org/licenses/mit-license.php 

7 

8"""Collection classes and helpers.""" 

9 

10from __future__ import absolute_import 

11 

12import operator 

13import types 

14import weakref 

15 

16from .compat import binary_types 

17from .compat import collections_abc 

18from .compat import itertools_filterfalse 

19from .compat import py2k 

20from .compat import string_types 

21from .compat import threading 

22 

23 

24EMPTY_SET = frozenset() 

25 

26 

27class AbstractKeyedTuple(tuple): 

28 __slots__ = () 

29 

30 def keys(self): 

31 """Return a list of string key names for this :class:`.KeyedTuple`. 

32 

33 .. seealso:: 

34 

35 :attr:`.KeyedTuple._fields` 

36 

37 """ 

38 

39 return list(self._fields) 

40 

41 

42class KeyedTuple(AbstractKeyedTuple): 

43 """``tuple`` subclass that adds labeled names. 

44 

45 E.g.:: 

46 

47 >>> k = KeyedTuple([1, 2, 3], labels=["one", "two", "three"]) 

48 >>> k.one 

49 1 

50 >>> k.two 

51 2 

52 

53 Result rows returned by :class:`_query.Query` that contain multiple 

54 ORM entities and/or column expressions make use of this 

55 class to return rows. 

56 

57 The :class:`.KeyedTuple` exhibits similar behavior to the 

58 ``collections.namedtuple()`` construct provided in the Python 

59 standard library, however is architected very differently. 

60 Unlike ``collections.namedtuple()``, :class:`.KeyedTuple` is 

61 does not rely on creation of custom subtypes in order to represent 

62 a new series of keys, instead each :class:`.KeyedTuple` instance 

63 receives its list of keys in place. The subtype approach 

64 of ``collections.namedtuple()`` introduces significant complexity 

65 and performance overhead, which is not necessary for the 

66 :class:`_query.Query` object's use case. 

67 

68 .. seealso:: 

69 

70 :ref:`ormtutorial_querying` 

71 

72 """ 

73 

74 def __new__(cls, vals, labels=None): 

75 t = tuple.__new__(cls, vals) 

76 if labels: 

77 t.__dict__.update(zip(labels, vals)) 

78 else: 

79 labels = [] 

80 t.__dict__["_labels"] = labels 

81 return t 

82 

83 @property 

84 def _fields(self): 

85 """Return a tuple of string key names for this :class:`.KeyedTuple`. 

86 

87 This method provides compatibility with ``collections.namedtuple()``. 

88 

89 .. seealso:: 

90 

91 :meth:`.KeyedTuple.keys` 

92 

93 """ 

94 return tuple([l for l in self._labels if l is not None]) 

95 

96 def __setattr__(self, key, value): 

97 raise AttributeError("Can't set attribute: %s" % key) 

98 

99 def _asdict(self): 

100 """Return the contents of this :class:`.KeyedTuple` as a dictionary. 

101 

102 This method provides compatibility with ``collections.namedtuple()``, 

103 with the exception that the dictionary returned is **not** ordered. 

104 

105 """ 

106 return {key: self.__dict__[key] for key in self.keys()} 

107 

108 

109class _LW(AbstractKeyedTuple): 

110 __slots__ = () 

111 

112 def __new__(cls, vals): 

113 return tuple.__new__(cls, vals) 

114 

115 def __reduce__(self): 

116 # for pickling, degrade down to the regular 

117 # KeyedTuple, thus avoiding anonymous class pickling 

118 # difficulties 

119 return KeyedTuple, (list(self), self._real_fields) 

120 

121 def _asdict(self): 

122 """Return the contents of this :class:`.KeyedTuple` as a dictionary.""" 

123 

124 d = dict(zip(self._real_fields, self)) 

125 d.pop(None, None) 

126 return d 

127 

128 

129class ImmutableContainer(object): 

130 def _immutable(self, *arg, **kw): 

131 raise TypeError("%s object is immutable" % self.__class__.__name__) 

132 

133 __delitem__ = __setitem__ = __setattr__ = _immutable 

134 

135 

136class immutabledict(ImmutableContainer, dict): 

137 

138 clear = pop = popitem = setdefault = update = ImmutableContainer._immutable 

139 

140 def __new__(cls, *args): 

141 new = dict.__new__(cls) 

142 dict.__init__(new, *args) 

143 return new 

144 

145 def __init__(self, *args): 

146 pass 

147 

148 def __reduce__(self): 

149 return immutabledict, (dict(self),) 

150 

151 def union(self, d): 

152 if not d: 

153 return self 

154 elif not self: 

155 if isinstance(d, immutabledict): 

156 return d 

157 else: 

158 return immutabledict(d) 

159 else: 

160 d2 = immutabledict(self) 

161 dict.update(d2, d) 

162 return d2 

163 

164 def __repr__(self): 

165 return "immutabledict(%s)" % dict.__repr__(self) 

166 

167 

168class Properties(object): 

169 """Provide a __getattr__/__setattr__ interface over a dict.""" 

170 

171 __slots__ = ("_data",) 

172 

173 def __init__(self, data): 

174 object.__setattr__(self, "_data", data) 

175 

176 def __len__(self): 

177 return len(self._data) 

178 

179 def __iter__(self): 

180 return iter(list(self._data.values())) 

181 

182 def __dir__(self): 

183 return dir(super(Properties, self)) + [ 

184 str(k) for k in self._data.keys() 

185 ] 

186 

187 def __add__(self, other): 

188 return list(self) + list(other) 

189 

190 def __setitem__(self, key, obj): 

191 self._data[key] = obj 

192 

193 def __getitem__(self, key): 

194 return self._data[key] 

195 

196 def __delitem__(self, key): 

197 del self._data[key] 

198 

199 def __setattr__(self, key, obj): 

200 self._data[key] = obj 

201 

202 def __getstate__(self): 

203 return {"_data": self._data} 

204 

205 def __setstate__(self, state): 

206 object.__setattr__(self, "_data", state["_data"]) 

207 

208 def __getattr__(self, key): 

209 try: 

210 return self._data[key] 

211 except KeyError: 

212 raise AttributeError(key) 

213 

214 def __contains__(self, key): 

215 return key in self._data 

216 

217 def as_immutable(self): 

218 """Return an immutable proxy for this :class:`.Properties`.""" 

219 

220 return ImmutableProperties(self._data) 

221 

222 def update(self, value): 

223 self._data.update(value) 

224 

225 def get(self, key, default=None): 

226 if key in self: 

227 return self[key] 

228 else: 

229 return default 

230 

231 def keys(self): 

232 return list(self._data) 

233 

234 def values(self): 

235 return list(self._data.values()) 

236 

237 def items(self): 

238 return list(self._data.items()) 

239 

240 def has_key(self, key): 

241 return key in self._data 

242 

243 def clear(self): 

244 self._data.clear() 

245 

246 

247class OrderedProperties(Properties): 

248 """Provide a __getattr__/__setattr__ interface with an OrderedDict 

249 as backing store.""" 

250 

251 __slots__ = () 

252 

253 def __init__(self): 

254 Properties.__init__(self, OrderedDict()) 

255 

256 

257class ImmutableProperties(ImmutableContainer, Properties): 

258 """Provide immutable dict/object attribute to an underlying dictionary.""" 

259 

260 __slots__ = () 

261 

262 

263class OrderedDict(dict): 

264 """A dict that returns keys/values/items in the order they were added.""" 

265 

266 __slots__ = ("_list",) 

267 

268 def __reduce__(self): 

269 return OrderedDict, (self.items(),) 

270 

271 def __init__(self, ____sequence=None, **kwargs): 

272 self._list = [] 

273 if ____sequence is None: 

274 if kwargs: 

275 self.update(**kwargs) 

276 else: 

277 self.update(____sequence, **kwargs) 

278 

279 def clear(self): 

280 self._list = [] 

281 dict.clear(self) 

282 

283 def copy(self): 

284 return self.__copy__() 

285 

286 def __copy__(self): 

287 return OrderedDict(self) 

288 

289 def sort(self, *arg, **kw): 

290 self._list.sort(*arg, **kw) 

291 

292 def update(self, ____sequence=None, **kwargs): 

293 if ____sequence is not None: 

294 if hasattr(____sequence, "keys"): 

295 for key in ____sequence.keys(): 

296 self.__setitem__(key, ____sequence[key]) 

297 else: 

298 for key, value in ____sequence: 

299 self[key] = value 

300 if kwargs: 

301 self.update(kwargs) 

302 

303 def setdefault(self, key, value): 

304 if key not in self: 

305 self.__setitem__(key, value) 

306 return value 

307 else: 

308 return self.__getitem__(key) 

309 

310 def __iter__(self): 

311 return iter(self._list) 

312 

313 def keys(self): 

314 return list(self) 

315 

316 def values(self): 

317 return [self[key] for key in self._list] 

318 

319 def items(self): 

320 return [(key, self[key]) for key in self._list] 

321 

322 if py2k: 

323 

324 def itervalues(self): 

325 return iter(self.values()) 

326 

327 def iterkeys(self): 

328 return iter(self) 

329 

330 def iteritems(self): 

331 return iter(self.items()) 

332 

333 def __setitem__(self, key, obj): 

334 if key not in self: 

335 try: 

336 self._list.append(key) 

337 except AttributeError: 

338 # work around Python pickle loads() with 

339 # dict subclass (seems to ignore __setstate__?) 

340 self._list = [key] 

341 dict.__setitem__(self, key, obj) 

342 

343 def __delitem__(self, key): 

344 dict.__delitem__(self, key) 

345 self._list.remove(key) 

346 

347 def pop(self, key, *default): 

348 present = key in self 

349 value = dict.pop(self, key, *default) 

350 if present: 

351 self._list.remove(key) 

352 return value 

353 

354 def popitem(self): 

355 item = dict.popitem(self) 

356 self._list.remove(item[0]) 

357 return item 

358 

359 

360class OrderedSet(set): 

361 def __init__(self, d=None): 

362 set.__init__(self) 

363 self._list = [] 

364 if d is not None: 

365 self._list = unique_list(d) 

366 set.update(self, self._list) 

367 else: 

368 self._list = [] 

369 

370 def add(self, element): 

371 if element not in self: 

372 self._list.append(element) 

373 set.add(self, element) 

374 

375 def remove(self, element): 

376 set.remove(self, element) 

377 self._list.remove(element) 

378 

379 def insert(self, pos, element): 

380 if element not in self: 

381 self._list.insert(pos, element) 

382 set.add(self, element) 

383 

384 def discard(self, element): 

385 if element in self: 

386 self._list.remove(element) 

387 set.remove(self, element) 

388 

389 def clear(self): 

390 set.clear(self) 

391 self._list = [] 

392 

393 def __getitem__(self, key): 

394 return self._list[key] 

395 

396 def __iter__(self): 

397 return iter(self._list) 

398 

399 def __add__(self, other): 

400 return self.union(other) 

401 

402 def __repr__(self): 

403 return "%s(%r)" % (self.__class__.__name__, self._list) 

404 

405 __str__ = __repr__ 

406 

407 def update(self, iterable): 

408 for e in iterable: 

409 if e not in self: 

410 self._list.append(e) 

411 set.add(self, e) 

412 return self 

413 

414 __ior__ = update 

415 

416 def union(self, other): 

417 result = self.__class__(self) 

418 result.update(other) 

419 return result 

420 

421 __or__ = union 

422 

423 def intersection(self, other): 

424 other = set(other) 

425 return self.__class__(a for a in self if a in other) 

426 

427 __and__ = intersection 

428 

429 def symmetric_difference(self, other): 

430 other = set(other) 

431 result = self.__class__(a for a in self if a not in other) 

432 result.update(a for a in other if a not in self) 

433 return result 

434 

435 __xor__ = symmetric_difference 

436 

437 def difference(self, other): 

438 other = set(other) 

439 return self.__class__(a for a in self if a not in other) 

440 

441 __sub__ = difference 

442 

443 def intersection_update(self, other): 

444 other = set(other) 

445 set.intersection_update(self, other) 

446 self._list = [a for a in self._list if a in other] 

447 return self 

448 

449 __iand__ = intersection_update 

450 

451 def symmetric_difference_update(self, other): 

452 set.symmetric_difference_update(self, other) 

453 self._list = [a for a in self._list if a in self] 

454 self._list += [a for a in other._list if a in self] 

455 return self 

456 

457 __ixor__ = symmetric_difference_update 

458 

459 def difference_update(self, other): 

460 set.difference_update(self, other) 

461 self._list = [a for a in self._list if a in self] 

462 return self 

463 

464 __isub__ = difference_update 

465 

466 

467class IdentitySet(object): 

468 """A set that considers only object id() for uniqueness. 

469 

470 This strategy has edge cases for builtin types- it's possible to have 

471 two 'foo' strings in one of these sets, for example. Use sparingly. 

472 

473 """ 

474 

475 def __init__(self, iterable=None): 

476 self._members = dict() 

477 if iterable: 

478 self.update(iterable) 

479 

480 def add(self, value): 

481 self._members[id(value)] = value 

482 

483 def __contains__(self, value): 

484 return id(value) in self._members 

485 

486 def remove(self, value): 

487 del self._members[id(value)] 

488 

489 def discard(self, value): 

490 try: 

491 self.remove(value) 

492 except KeyError: 

493 pass 

494 

495 def pop(self): 

496 try: 

497 pair = self._members.popitem() 

498 return pair[1] 

499 except KeyError: 

500 raise KeyError("pop from an empty set") 

501 

502 def clear(self): 

503 self._members.clear() 

504 

505 def __cmp__(self, other): 

506 raise TypeError("cannot compare sets using cmp()") 

507 

508 def __eq__(self, other): 

509 if isinstance(other, IdentitySet): 

510 return self._members == other._members 

511 else: 

512 return False 

513 

514 def __ne__(self, other): 

515 if isinstance(other, IdentitySet): 

516 return self._members != other._members 

517 else: 

518 return True 

519 

520 def issubset(self, iterable): 

521 other = self.__class__(iterable) 

522 

523 if len(self) > len(other): 

524 return False 

525 for m in itertools_filterfalse( 

526 other._members.__contains__, iter(self._members.keys()) 

527 ): 

528 return False 

529 return True 

530 

531 def __le__(self, other): 

532 if not isinstance(other, IdentitySet): 

533 return NotImplemented 

534 return self.issubset(other) 

535 

536 def __lt__(self, other): 

537 if not isinstance(other, IdentitySet): 

538 return NotImplemented 

539 return len(self) < len(other) and self.issubset(other) 

540 

541 def issuperset(self, iterable): 

542 other = self.__class__(iterable) 

543 

544 if len(self) < len(other): 

545 return False 

546 

547 for m in itertools_filterfalse( 

548 self._members.__contains__, iter(other._members.keys()) 

549 ): 

550 return False 

551 return True 

552 

553 def __ge__(self, other): 

554 if not isinstance(other, IdentitySet): 

555 return NotImplemented 

556 return self.issuperset(other) 

557 

558 def __gt__(self, other): 

559 if not isinstance(other, IdentitySet): 

560 return NotImplemented 

561 return len(self) > len(other) and self.issuperset(other) 

562 

563 def union(self, iterable): 

564 result = self.__class__() 

565 members = self._members 

566 result._members.update(members) 

567 result._members.update((id(obj), obj) for obj in iterable) 

568 return result 

569 

570 def __or__(self, other): 

571 if not isinstance(other, IdentitySet): 

572 return NotImplemented 

573 return self.union(other) 

574 

575 def update(self, iterable): 

576 self._members.update((id(obj), obj) for obj in iterable) 

577 

578 def __ior__(self, other): 

579 if not isinstance(other, IdentitySet): 

580 return NotImplemented 

581 self.update(other) 

582 return self 

583 

584 def difference(self, iterable): 

585 result = self.__class__() 

586 members = self._members 

587 other = {id(obj) for obj in iterable} 

588 result._members.update( 

589 ((k, v) for k, v in members.items() if k not in other) 

590 ) 

591 return result 

592 

593 def __sub__(self, other): 

594 if not isinstance(other, IdentitySet): 

595 return NotImplemented 

596 return self.difference(other) 

597 

598 def difference_update(self, iterable): 

599 self._members = self.difference(iterable)._members 

600 

601 def __isub__(self, other): 

602 if not isinstance(other, IdentitySet): 

603 return NotImplemented 

604 self.difference_update(other) 

605 return self 

606 

607 def intersection(self, iterable): 

608 result = self.__class__() 

609 members = self._members 

610 other = {id(obj) for obj in iterable} 

611 result._members.update( 

612 (k, v) for k, v in members.items() if k in other 

613 ) 

614 return result 

615 

616 def __and__(self, other): 

617 if not isinstance(other, IdentitySet): 

618 return NotImplemented 

619 return self.intersection(other) 

620 

621 def intersection_update(self, iterable): 

622 self._members = self.intersection(iterable)._members 

623 

624 def __iand__(self, other): 

625 if not isinstance(other, IdentitySet): 

626 return NotImplemented 

627 self.intersection_update(other) 

628 return self 

629 

630 def symmetric_difference(self, iterable): 

631 result = self.__class__() 

632 members = self._members 

633 other = {id(obj): obj for obj in iterable} 

634 result._members.update( 

635 ((k, v) for k, v in members.items() if k not in other) 

636 ) 

637 result._members.update( 

638 ((k, v) for k, v in other.items() if k not in members) 

639 ) 

640 return result 

641 

642 def __xor__(self, other): 

643 if not isinstance(other, IdentitySet): 

644 return NotImplemented 

645 return self.symmetric_difference(other) 

646 

647 def symmetric_difference_update(self, iterable): 

648 self._members = self.symmetric_difference(iterable)._members 

649 

650 def __ixor__(self, other): 

651 if not isinstance(other, IdentitySet): 

652 return NotImplemented 

653 self.symmetric_difference(other) 

654 return self 

655 

656 def copy(self): 

657 return type(self)(iter(self._members.values())) 

658 

659 __copy__ = copy 

660 

661 def __len__(self): 

662 return len(self._members) 

663 

664 def __iter__(self): 

665 return iter(self._members.values()) 

666 

667 def __hash__(self): 

668 raise TypeError("set objects are unhashable") 

669 

670 def __repr__(self): 

671 return "%s(%r)" % (type(self).__name__, list(self._members.values())) 

672 

673 

674class WeakSequence(object): 

675 def __init__(self, __elements=()): 

676 # adapted from weakref.WeakKeyDictionary, prevent reference 

677 # cycles in the collection itself 

678 def _remove(item, selfref=weakref.ref(self)): 

679 self = selfref() 

680 if self is not None: 

681 self._storage.remove(item) 

682 

683 self._remove = _remove 

684 self._storage = [ 

685 weakref.ref(element, _remove) for element in __elements 

686 ] 

687 

688 def append(self, item): 

689 self._storage.append(weakref.ref(item, self._remove)) 

690 

691 def __len__(self): 

692 return len(self._storage) 

693 

694 def __iter__(self): 

695 return ( 

696 obj for obj in (ref() for ref in self._storage) if obj is not None 

697 ) 

698 

699 def __getitem__(self, index): 

700 try: 

701 obj = self._storage[index] 

702 except KeyError: 

703 raise IndexError("Index %s out of range" % index) 

704 else: 

705 return obj() 

706 

707 

708class OrderedIdentitySet(IdentitySet): 

709 def __init__(self, iterable=None): 

710 IdentitySet.__init__(self) 

711 self._members = OrderedDict() 

712 if iterable: 

713 for o in iterable: 

714 self.add(o) 

715 

716 

717class PopulateDict(dict): 

718 """A dict which populates missing values via a creation function. 

719 

720 Note the creation function takes a key, unlike 

721 collections.defaultdict. 

722 

723 """ 

724 

725 def __init__(self, creator): 

726 self.creator = creator 

727 

728 def __missing__(self, key): 

729 self[key] = val = self.creator(key) 

730 return val 

731 

732 

733class WeakPopulateDict(dict): 

734 """Like PopulateDict, but assumes a self + a method and does not create 

735 a reference cycle. 

736 

737 """ 

738 

739 def __init__(self, creator_method): 

740 self.creator = creator_method.__func__ 

741 weakself = creator_method.__self__ 

742 self.weakself = weakref.ref(weakself) 

743 

744 def __missing__(self, key): 

745 self[key] = val = self.creator(self.weakself(), key) 

746 return val 

747 

748 

749# Define collections that are capable of storing 

750# ColumnElement objects as hashable keys/elements. 

751# At this point, these are mostly historical, things 

752# used to be more complicated. 

753column_set = set 

754column_dict = dict 

755ordered_column_set = OrderedSet 

756 

757 

758_getters = PopulateDict(operator.itemgetter) 

759 

760_property_getters = PopulateDict( 

761 lambda idx: property(operator.itemgetter(idx)) 

762) 

763 

764 

765def unique_list(seq, hashfunc=None): 

766 seen = set() 

767 seen_add = seen.add 

768 if not hashfunc: 

769 return [x for x in seq if x not in seen and not seen_add(x)] 

770 else: 

771 return [ 

772 x 

773 for x in seq 

774 if hashfunc(x) not in seen and not seen_add(hashfunc(x)) 

775 ] 

776 

777 

778class UniqueAppender(object): 

779 """Appends items to a collection ensuring uniqueness. 

780 

781 Additional appends() of the same object are ignored. Membership is 

782 determined by identity (``is a``) not equality (``==``). 

783 """ 

784 

785 def __init__(self, data, via=None): 

786 self.data = data 

787 self._unique = {} 

788 if via: 

789 self._data_appender = getattr(data, via) 

790 elif hasattr(data, "append"): 

791 self._data_appender = data.append 

792 elif hasattr(data, "add"): 

793 self._data_appender = data.add 

794 

795 def append(self, item): 

796 id_ = id(item) 

797 if id_ not in self._unique: 

798 self._data_appender(item) 

799 self._unique[id_] = True 

800 

801 def __iter__(self): 

802 return iter(self.data) 

803 

804 

805def coerce_generator_arg(arg): 

806 if len(arg) == 1 and isinstance(arg[0], types.GeneratorType): 

807 return list(arg[0]) 

808 else: 

809 return arg 

810 

811 

812def to_list(x, default=None): 

813 if x is None: 

814 return default 

815 if not isinstance(x, collections_abc.Iterable) or isinstance( 

816 x, string_types + binary_types 

817 ): 

818 return [x] 

819 elif isinstance(x, list): 

820 return x 

821 else: 

822 return list(x) 

823 

824 

825def has_intersection(set_, iterable): 

826 r"""return True if any items of set\_ are present in iterable. 

827 

828 Goes through special effort to ensure __hash__ is not called 

829 on items in iterable that don't support it. 

830 

831 """ 

832 # TODO: optimize, write in C, etc. 

833 return bool(set_.intersection([i for i in iterable if i.__hash__])) 

834 

835 

836def to_set(x): 

837 if x is None: 

838 return set() 

839 if not isinstance(x, set): 

840 return set(to_list(x)) 

841 else: 

842 return x 

843 

844 

845def to_column_set(x): 

846 if x is None: 

847 return column_set() 

848 if not isinstance(x, column_set): 

849 return column_set(to_list(x)) 

850 else: 

851 return x 

852 

853 

854def update_copy(d, _new=None, **kw): 

855 """Copy the given dict and update with the given values.""" 

856 

857 d = d.copy() 

858 if _new: 

859 d.update(_new) 

860 d.update(**kw) 

861 return d 

862 

863 

864def flatten_iterator(x): 

865 """Given an iterator of which further sub-elements may also be 

866 iterators, flatten the sub-elements into a single iterator. 

867 

868 """ 

869 for elem in x: 

870 if not isinstance(elem, str) and hasattr(elem, "__iter__"): 

871 for y in flatten_iterator(elem): 

872 yield y 

873 else: 

874 yield elem 

875 

876 

877class LRUCache(dict): 

878 """Dictionary with 'squishy' removal of least 

879 recently used items. 

880 

881 Note that either get() or [] should be used here, but 

882 generally its not safe to do an "in" check first as the dictionary 

883 can change subsequent to that call. 

884 

885 """ 

886 

887 __slots__ = "capacity", "threshold", "size_alert", "_counter", "_mutex" 

888 

889 def __init__(self, capacity=100, threshold=0.5, size_alert=None): 

890 self.capacity = capacity 

891 self.threshold = threshold 

892 self.size_alert = size_alert 

893 self._counter = 0 

894 self._mutex = threading.Lock() 

895 

896 def _inc_counter(self): 

897 self._counter += 1 

898 return self._counter 

899 

900 def get(self, key, default=None): 

901 item = dict.get(self, key, default) 

902 if item is not default: 

903 item[2] = self._inc_counter() 

904 return item[1] 

905 else: 

906 return default 

907 

908 def __getitem__(self, key): 

909 item = dict.__getitem__(self, key) 

910 item[2] = self._inc_counter() 

911 return item[1] 

912 

913 def values(self): 

914 return [i[1] for i in dict.values(self)] 

915 

916 def setdefault(self, key, value): 

917 if key in self: 

918 return self[key] 

919 else: 

920 self[key] = value 

921 return value 

922 

923 def __setitem__(self, key, value): 

924 item = dict.get(self, key) 

925 if item is None: 

926 item = [key, value, self._inc_counter()] 

927 dict.__setitem__(self, key, item) 

928 else: 

929 item[1] = value 

930 self._manage_size() 

931 

932 @property 

933 def size_threshold(self): 

934 return self.capacity + self.capacity * self.threshold 

935 

936 def _manage_size(self): 

937 if not self._mutex.acquire(False): 

938 return 

939 try: 

940 size_alert = bool(self.size_alert) 

941 while len(self) > self.capacity + self.capacity * self.threshold: 

942 if size_alert: 

943 size_alert = False 

944 self.size_alert(self) 

945 by_counter = sorted( 

946 dict.values(self), key=operator.itemgetter(2), reverse=True 

947 ) 

948 for item in by_counter[self.capacity :]: 

949 try: 

950 del self[item[0]] 

951 except KeyError: 

952 # deleted elsewhere; skip 

953 continue 

954 finally: 

955 self._mutex.release() 

956 

957 

958_lw_tuples = LRUCache(100) 

959 

960 

961def lightweight_named_tuple(name, fields): 

962 hash_ = (name,) + tuple(fields) 

963 tp_cls = _lw_tuples.get(hash_) 

964 if tp_cls: 

965 return tp_cls 

966 

967 tp_cls = type( 

968 name, 

969 (_LW,), 

970 dict( 

971 [ 

972 (field, _property_getters[idx]) 

973 for idx, field in enumerate(fields) 

974 if field is not None 

975 ] 

976 + [("__slots__", ())] 

977 ), 

978 ) 

979 

980 tp_cls._real_fields = fields 

981 tp_cls._fields = tuple([f for f in fields if f is not None]) 

982 

983 _lw_tuples[hash_] = tp_cls 

984 return tp_cls 

985 

986 

987class ScopedRegistry(object): 

988 """A Registry that can store one or multiple instances of a single 

989 class on the basis of a "scope" function. 

990 

991 The object implements ``__call__`` as the "getter", so by 

992 calling ``myregistry()`` the contained object is returned 

993 for the current scope. 

994 

995 :param createfunc: 

996 a callable that returns a new object to be placed in the registry 

997 

998 :param scopefunc: 

999 a callable that will return a key to store/retrieve an object. 

1000 """ 

1001 

1002 def __init__(self, createfunc, scopefunc): 

1003 """Construct a new :class:`.ScopedRegistry`. 

1004 

1005 :param createfunc: A creation function that will generate 

1006 a new value for the current scope, if none is present. 

1007 

1008 :param scopefunc: A function that returns a hashable 

1009 token representing the current scope (such as, current 

1010 thread identifier). 

1011 

1012 """ 

1013 self.createfunc = createfunc 

1014 self.scopefunc = scopefunc 

1015 self.registry = {} 

1016 

1017 def __call__(self): 

1018 key = self.scopefunc() 

1019 try: 

1020 return self.registry[key] 

1021 except KeyError: 

1022 return self.registry.setdefault(key, self.createfunc()) 

1023 

1024 def has(self): 

1025 """Return True if an object is present in the current scope.""" 

1026 

1027 return self.scopefunc() in self.registry 

1028 

1029 def set(self, obj): 

1030 """Set the value for the current scope.""" 

1031 

1032 self.registry[self.scopefunc()] = obj 

1033 

1034 def clear(self): 

1035 """Clear the current scope, if any.""" 

1036 

1037 try: 

1038 del self.registry[self.scopefunc()] 

1039 except KeyError: 

1040 pass 

1041 

1042 

1043class ThreadLocalRegistry(ScopedRegistry): 

1044 """A :class:`.ScopedRegistry` that uses a ``threading.local()`` 

1045 variable for storage. 

1046 

1047 """ 

1048 

1049 def __init__(self, createfunc): 

1050 self.createfunc = createfunc 

1051 self.registry = threading.local() 

1052 

1053 def __call__(self): 

1054 try: 

1055 return self.registry.value 

1056 except AttributeError: 

1057 val = self.registry.value = self.createfunc() 

1058 return val 

1059 

1060 def has(self): 

1061 return hasattr(self.registry, "value") 

1062 

1063 def set(self, obj): 

1064 self.registry.value = obj 

1065 

1066 def clear(self): 

1067 try: 

1068 del self.registry.value 

1069 except AttributeError: 

1070 pass 

1071 

1072 

1073def has_dupes(sequence, target): 

1074 """Given a sequence and search object, return True if there's more 

1075 than one, False if zero or one of them. 

1076 

1077 

1078 """ 

1079 # compare to .index version below, this version introduces less function 

1080 # overhead and is usually the same speed. At 15000 items (way bigger than 

1081 # a relationship-bound collection in memory usually is) it begins to 

1082 # fall behind the other version only by microseconds. 

1083 c = 0 

1084 for item in sequence: 

1085 if item is target: 

1086 c += 1 

1087 if c > 1: 

1088 return True 

1089 return False 

1090 

1091 

1092# .index version. the two __contains__ calls as well 

1093# as .index() and isinstance() slow this down. 

1094# def has_dupes(sequence, target): 

1095# if target not in sequence: 

1096# return False 

1097# elif not isinstance(sequence, collections_abc.Sequence): 

1098# return False 

1099# 

1100# idx = sequence.index(target) 

1101# return target in sequence[idx + 1:]