_collections.py 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. # util/_collections.py
  2. # Copyright (C) 2005-2017 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. """Collection classes and helpers."""
  8. from __future__ import absolute_import
  9. import weakref
  10. import operator
  11. from .compat import threading, itertools_filterfalse, string_types, \
  12. binary_types
  13. from . import py2k
  14. import types
  15. import collections
  16. EMPTY_SET = frozenset()
  17. class AbstractKeyedTuple(tuple):
  18. __slots__ = ()
  19. def keys(self):
  20. """Return a list of string key names for this :class:`.KeyedTuple`.
  21. .. seealso::
  22. :attr:`.KeyedTuple._fields`
  23. """
  24. return list(self._fields)
  25. class KeyedTuple(AbstractKeyedTuple):
  26. """``tuple`` subclass that adds labeled names.
  27. E.g.::
  28. >>> k = KeyedTuple([1, 2, 3], labels=["one", "two", "three"])
  29. >>> k.one
  30. 1
  31. >>> k.two
  32. 2
  33. Result rows returned by :class:`.Query` that contain multiple
  34. ORM entities and/or column expressions make use of this
  35. class to return rows.
  36. The :class:`.KeyedTuple` exhibits similar behavior to the
  37. ``collections.namedtuple()`` construct provided in the Python
  38. standard library, however is architected very differently.
  39. Unlike ``collections.namedtuple()``, :class:`.KeyedTuple` is
  40. does not rely on creation of custom subtypes in order to represent
  41. a new series of keys, instead each :class:`.KeyedTuple` instance
  42. receives its list of keys in place. The subtype approach
  43. of ``collections.namedtuple()`` introduces significant complexity
  44. and performance overhead, which is not necessary for the
  45. :class:`.Query` object's use case.
  46. .. versionchanged:: 0.8
  47. Compatibility methods with ``collections.namedtuple()`` have been
  48. added including :attr:`.KeyedTuple._fields` and
  49. :meth:`.KeyedTuple._asdict`.
  50. .. seealso::
  51. :ref:`ormtutorial_querying`
  52. """
  53. def __new__(cls, vals, labels=None):
  54. t = tuple.__new__(cls, vals)
  55. if labels:
  56. t.__dict__.update(zip(labels, vals))
  57. else:
  58. labels = []
  59. t.__dict__['_labels'] = labels
  60. return t
  61. @property
  62. def _fields(self):
  63. """Return a tuple of string key names for this :class:`.KeyedTuple`.
  64. This method provides compatibility with ``collections.namedtuple()``.
  65. .. versionadded:: 0.8
  66. .. seealso::
  67. :meth:`.KeyedTuple.keys`
  68. """
  69. return tuple([l for l in self._labels if l is not None])
  70. def __setattr__(self, key, value):
  71. raise AttributeError("Can't set attribute: %s" % key)
  72. def _asdict(self):
  73. """Return the contents of this :class:`.KeyedTuple` as a dictionary.
  74. This method provides compatibility with ``collections.namedtuple()``,
  75. with the exception that the dictionary returned is **not** ordered.
  76. .. versionadded:: 0.8
  77. """
  78. return dict((key, self.__dict__[key]) for key in self.keys())
  79. class _LW(AbstractKeyedTuple):
  80. __slots__ = ()
  81. def __new__(cls, vals):
  82. return tuple.__new__(cls, vals)
  83. def __reduce__(self):
  84. # for pickling, degrade down to the regular
  85. # KeyedTuple, thus avoiding anonymous class pickling
  86. # difficulties
  87. return KeyedTuple, (list(self), self._real_fields)
  88. def _asdict(self):
  89. """Return the contents of this :class:`.KeyedTuple` as a dictionary."""
  90. d = dict(zip(self._real_fields, self))
  91. d.pop(None, None)
  92. return d
  93. class ImmutableContainer(object):
  94. def _immutable(self, *arg, **kw):
  95. raise TypeError("%s object is immutable" % self.__class__.__name__)
  96. __delitem__ = __setitem__ = __setattr__ = _immutable
  97. class immutabledict(ImmutableContainer, dict):
  98. clear = pop = popitem = setdefault = \
  99. update = ImmutableContainer._immutable
  100. def __new__(cls, *args):
  101. new = dict.__new__(cls)
  102. dict.__init__(new, *args)
  103. return new
  104. def __init__(self, *args):
  105. pass
  106. def __reduce__(self):
  107. return immutabledict, (dict(self), )
  108. def union(self, d):
  109. if not d:
  110. return self
  111. elif not self:
  112. if isinstance(d, immutabledict):
  113. return d
  114. else:
  115. return immutabledict(d)
  116. else:
  117. d2 = immutabledict(self)
  118. dict.update(d2, d)
  119. return d2
  120. def __repr__(self):
  121. return "immutabledict(%s)" % dict.__repr__(self)
  122. class Properties(object):
  123. """Provide a __getattr__/__setattr__ interface over a dict."""
  124. __slots__ = '_data',
  125. def __init__(self, data):
  126. object.__setattr__(self, '_data', data)
  127. def __len__(self):
  128. return len(self._data)
  129. def __iter__(self):
  130. return iter(list(self._data.values()))
  131. def __add__(self, other):
  132. return list(self) + list(other)
  133. def __setitem__(self, key, object):
  134. self._data[key] = object
  135. def __getitem__(self, key):
  136. return self._data[key]
  137. def __delitem__(self, key):
  138. del self._data[key]
  139. def __setattr__(self, key, obj):
  140. self._data[key] = obj
  141. def __getstate__(self):
  142. return {'_data': self._data}
  143. def __setstate__(self, state):
  144. object.__setattr__(self, '_data', state['_data'])
  145. def __getattr__(self, key):
  146. try:
  147. return self._data[key]
  148. except KeyError:
  149. raise AttributeError(key)
  150. def __contains__(self, key):
  151. return key in self._data
  152. def as_immutable(self):
  153. """Return an immutable proxy for this :class:`.Properties`."""
  154. return ImmutableProperties(self._data)
  155. def update(self, value):
  156. self._data.update(value)
  157. def get(self, key, default=None):
  158. if key in self:
  159. return self[key]
  160. else:
  161. return default
  162. def keys(self):
  163. return list(self._data)
  164. def values(self):
  165. return list(self._data.values())
  166. def items(self):
  167. return list(self._data.items())
  168. def has_key(self, key):
  169. return key in self._data
  170. def clear(self):
  171. self._data.clear()
  172. class OrderedProperties(Properties):
  173. """Provide a __getattr__/__setattr__ interface with an OrderedDict
  174. as backing store."""
  175. __slots__ = ()
  176. def __init__(self):
  177. Properties.__init__(self, OrderedDict())
  178. class ImmutableProperties(ImmutableContainer, Properties):
  179. """Provide immutable dict/object attribute to an underlying dictionary."""
  180. __slots__ = ()
  181. class OrderedDict(dict):
  182. """A dict that returns keys/values/items in the order they were added."""
  183. __slots__ = '_list',
  184. def __reduce__(self):
  185. return OrderedDict, (self.items(),)
  186. def __init__(self, ____sequence=None, **kwargs):
  187. self._list = []
  188. if ____sequence is None:
  189. if kwargs:
  190. self.update(**kwargs)
  191. else:
  192. self.update(____sequence, **kwargs)
  193. def clear(self):
  194. self._list = []
  195. dict.clear(self)
  196. def copy(self):
  197. return self.__copy__()
  198. def __copy__(self):
  199. return OrderedDict(self)
  200. def sort(self, *arg, **kw):
  201. self._list.sort(*arg, **kw)
  202. def update(self, ____sequence=None, **kwargs):
  203. if ____sequence is not None:
  204. if hasattr(____sequence, 'keys'):
  205. for key in ____sequence.keys():
  206. self.__setitem__(key, ____sequence[key])
  207. else:
  208. for key, value in ____sequence:
  209. self[key] = value
  210. if kwargs:
  211. self.update(kwargs)
  212. def setdefault(self, key, value):
  213. if key not in self:
  214. self.__setitem__(key, value)
  215. return value
  216. else:
  217. return self.__getitem__(key)
  218. def __iter__(self):
  219. return iter(self._list)
  220. def keys(self):
  221. return list(self)
  222. def values(self):
  223. return [self[key] for key in self._list]
  224. def items(self):
  225. return [(key, self[key]) for key in self._list]
  226. if py2k:
  227. def itervalues(self):
  228. return iter(self.values())
  229. def iterkeys(self):
  230. return iter(self)
  231. def iteritems(self):
  232. return iter(self.items())
  233. def __setitem__(self, key, object):
  234. if key not in self:
  235. try:
  236. self._list.append(key)
  237. except AttributeError:
  238. # work around Python pickle loads() with
  239. # dict subclass (seems to ignore __setstate__?)
  240. self._list = [key]
  241. dict.__setitem__(self, key, object)
  242. def __delitem__(self, key):
  243. dict.__delitem__(self, key)
  244. self._list.remove(key)
  245. def pop(self, key, *default):
  246. present = key in self
  247. value = dict.pop(self, key, *default)
  248. if present:
  249. self._list.remove(key)
  250. return value
  251. def popitem(self):
  252. item = dict.popitem(self)
  253. self._list.remove(item[0])
  254. return item
  255. class OrderedSet(set):
  256. def __init__(self, d=None):
  257. set.__init__(self)
  258. self._list = []
  259. if d is not None:
  260. self._list = unique_list(d)
  261. set.update(self, self._list)
  262. else:
  263. self._list = []
  264. def add(self, element):
  265. if element not in self:
  266. self._list.append(element)
  267. set.add(self, element)
  268. def remove(self, element):
  269. set.remove(self, element)
  270. self._list.remove(element)
  271. def insert(self, pos, element):
  272. if element not in self:
  273. self._list.insert(pos, element)
  274. set.add(self, element)
  275. def discard(self, element):
  276. if element in self:
  277. self._list.remove(element)
  278. set.remove(self, element)
  279. def clear(self):
  280. set.clear(self)
  281. self._list = []
  282. def __getitem__(self, key):
  283. return self._list[key]
  284. def __iter__(self):
  285. return iter(self._list)
  286. def __add__(self, other):
  287. return self.union(other)
  288. def __repr__(self):
  289. return '%s(%r)' % (self.__class__.__name__, self._list)
  290. __str__ = __repr__
  291. def update(self, iterable):
  292. for e in iterable:
  293. if e not in self:
  294. self._list.append(e)
  295. set.add(self, e)
  296. return self
  297. __ior__ = update
  298. def union(self, other):
  299. result = self.__class__(self)
  300. result.update(other)
  301. return result
  302. __or__ = union
  303. def intersection(self, other):
  304. other = set(other)
  305. return self.__class__(a for a in self if a in other)
  306. __and__ = intersection
  307. def symmetric_difference(self, other):
  308. other = set(other)
  309. result = self.__class__(a for a in self if a not in other)
  310. result.update(a for a in other if a not in self)
  311. return result
  312. __xor__ = symmetric_difference
  313. def difference(self, other):
  314. other = set(other)
  315. return self.__class__(a for a in self if a not in other)
  316. __sub__ = difference
  317. def intersection_update(self, other):
  318. other = set(other)
  319. set.intersection_update(self, other)
  320. self._list = [a for a in self._list if a in other]
  321. return self
  322. __iand__ = intersection_update
  323. def symmetric_difference_update(self, other):
  324. set.symmetric_difference_update(self, other)
  325. self._list = [a for a in self._list if a in self]
  326. self._list += [a for a in other._list if a in self]
  327. return self
  328. __ixor__ = symmetric_difference_update
  329. def difference_update(self, other):
  330. set.difference_update(self, other)
  331. self._list = [a for a in self._list if a in self]
  332. return self
  333. __isub__ = difference_update
  334. class IdentitySet(object):
  335. """A set that considers only object id() for uniqueness.
  336. This strategy has edge cases for builtin types- it's possible to have
  337. two 'foo' strings in one of these sets, for example. Use sparingly.
  338. """
  339. _working_set = set
  340. def __init__(self, iterable=None):
  341. self._members = dict()
  342. if iterable:
  343. for o in iterable:
  344. self.add(o)
  345. def add(self, value):
  346. self._members[id(value)] = value
  347. def __contains__(self, value):
  348. return id(value) in self._members
  349. def remove(self, value):
  350. del self._members[id(value)]
  351. def discard(self, value):
  352. try:
  353. self.remove(value)
  354. except KeyError:
  355. pass
  356. def pop(self):
  357. try:
  358. pair = self._members.popitem()
  359. return pair[1]
  360. except KeyError:
  361. raise KeyError('pop from an empty set')
  362. def clear(self):
  363. self._members.clear()
  364. def __cmp__(self, other):
  365. raise TypeError('cannot compare sets using cmp()')
  366. def __eq__(self, other):
  367. if isinstance(other, IdentitySet):
  368. return self._members == other._members
  369. else:
  370. return False
  371. def __ne__(self, other):
  372. if isinstance(other, IdentitySet):
  373. return self._members != other._members
  374. else:
  375. return True
  376. def issubset(self, iterable):
  377. other = type(self)(iterable)
  378. if len(self) > len(other):
  379. return False
  380. for m in itertools_filterfalse(other._members.__contains__,
  381. iter(self._members.keys())):
  382. return False
  383. return True
  384. def __le__(self, other):
  385. if not isinstance(other, IdentitySet):
  386. return NotImplemented
  387. return self.issubset(other)
  388. def __lt__(self, other):
  389. if not isinstance(other, IdentitySet):
  390. return NotImplemented
  391. return len(self) < len(other) and self.issubset(other)
  392. def issuperset(self, iterable):
  393. other = type(self)(iterable)
  394. if len(self) < len(other):
  395. return False
  396. for m in itertools_filterfalse(self._members.__contains__,
  397. iter(other._members.keys())):
  398. return False
  399. return True
  400. def __ge__(self, other):
  401. if not isinstance(other, IdentitySet):
  402. return NotImplemented
  403. return self.issuperset(other)
  404. def __gt__(self, other):
  405. if not isinstance(other, IdentitySet):
  406. return NotImplemented
  407. return len(self) > len(other) and self.issuperset(other)
  408. def union(self, iterable):
  409. result = type(self)()
  410. # testlib.pragma exempt:__hash__
  411. members = self._member_id_tuples()
  412. other = _iter_id(iterable)
  413. result._members.update(self._working_set(members).union(other))
  414. return result
  415. def __or__(self, other):
  416. if not isinstance(other, IdentitySet):
  417. return NotImplemented
  418. return self.union(other)
  419. def update(self, iterable):
  420. self._members = self.union(iterable)._members
  421. def __ior__(self, other):
  422. if not isinstance(other, IdentitySet):
  423. return NotImplemented
  424. self.update(other)
  425. return self
  426. def difference(self, iterable):
  427. result = type(self)()
  428. # testlib.pragma exempt:__hash__
  429. members = self._member_id_tuples()
  430. other = _iter_id(iterable)
  431. result._members.update(self._working_set(members).difference(other))
  432. return result
  433. def __sub__(self, other):
  434. if not isinstance(other, IdentitySet):
  435. return NotImplemented
  436. return self.difference(other)
  437. def difference_update(self, iterable):
  438. self._members = self.difference(iterable)._members
  439. def __isub__(self, other):
  440. if not isinstance(other, IdentitySet):
  441. return NotImplemented
  442. self.difference_update(other)
  443. return self
  444. def intersection(self, iterable):
  445. result = type(self)()
  446. # testlib.pragma exempt:__hash__
  447. members = self._member_id_tuples()
  448. other = _iter_id(iterable)
  449. result._members.update(self._working_set(members).intersection(other))
  450. return result
  451. def __and__(self, other):
  452. if not isinstance(other, IdentitySet):
  453. return NotImplemented
  454. return self.intersection(other)
  455. def intersection_update(self, iterable):
  456. self._members = self.intersection(iterable)._members
  457. def __iand__(self, other):
  458. if not isinstance(other, IdentitySet):
  459. return NotImplemented
  460. self.intersection_update(other)
  461. return self
  462. def symmetric_difference(self, iterable):
  463. result = type(self)()
  464. # testlib.pragma exempt:__hash__
  465. members = self._member_id_tuples()
  466. other = _iter_id(iterable)
  467. result._members.update(
  468. self._working_set(members).symmetric_difference(other))
  469. return result
  470. def _member_id_tuples(self):
  471. return ((id(v), v) for v in self._members.values())
  472. def __xor__(self, other):
  473. if not isinstance(other, IdentitySet):
  474. return NotImplemented
  475. return self.symmetric_difference(other)
  476. def symmetric_difference_update(self, iterable):
  477. self._members = self.symmetric_difference(iterable)._members
  478. def __ixor__(self, other):
  479. if not isinstance(other, IdentitySet):
  480. return NotImplemented
  481. self.symmetric_difference(other)
  482. return self
  483. def copy(self):
  484. return type(self)(iter(self._members.values()))
  485. __copy__ = copy
  486. def __len__(self):
  487. return len(self._members)
  488. def __iter__(self):
  489. return iter(self._members.values())
  490. def __hash__(self):
  491. raise TypeError('set objects are unhashable')
  492. def __repr__(self):
  493. return '%s(%r)' % (type(self).__name__, list(self._members.values()))
  494. class WeakSequence(object):
  495. def __init__(self, __elements=()):
  496. self._storage = [
  497. weakref.ref(element, self._remove) for element in __elements
  498. ]
  499. def append(self, item):
  500. self._storage.append(weakref.ref(item, self._remove))
  501. def _remove(self, ref):
  502. self._storage.remove(ref)
  503. def __len__(self):
  504. return len(self._storage)
  505. def __iter__(self):
  506. return (obj for obj in
  507. (ref() for ref in self._storage) if obj is not None)
  508. def __getitem__(self, index):
  509. try:
  510. obj = self._storage[index]
  511. except KeyError:
  512. raise IndexError("Index %s out of range" % index)
  513. else:
  514. return obj()
  515. class OrderedIdentitySet(IdentitySet):
  516. class _working_set(OrderedSet):
  517. # a testing pragma: exempt the OIDS working set from the test suite's
  518. # "never call the user's __hash__" assertions. this is a big hammer,
  519. # but it's safe here: IDS operates on (id, instance) tuples in the
  520. # working set.
  521. __sa_hash_exempt__ = True
  522. def __init__(self, iterable=None):
  523. IdentitySet.__init__(self)
  524. self._members = OrderedDict()
  525. if iterable:
  526. for o in iterable:
  527. self.add(o)
  528. class PopulateDict(dict):
  529. """A dict which populates missing values via a creation function.
  530. Note the creation function takes a key, unlike
  531. collections.defaultdict.
  532. """
  533. def __init__(self, creator):
  534. self.creator = creator
  535. def __missing__(self, key):
  536. self[key] = val = self.creator(key)
  537. return val
  538. # Define collections that are capable of storing
  539. # ColumnElement objects as hashable keys/elements.
  540. # At this point, these are mostly historical, things
  541. # used to be more complicated.
  542. column_set = set
  543. column_dict = dict
  544. ordered_column_set = OrderedSet
  545. populate_column_dict = PopulateDict
  546. _getters = PopulateDict(operator.itemgetter)
  547. _property_getters = PopulateDict(
  548. lambda idx: property(operator.itemgetter(idx)))
  549. def unique_list(seq, hashfunc=None):
  550. seen = set()
  551. seen_add = seen.add
  552. if not hashfunc:
  553. return [x for x in seq
  554. if x not in seen
  555. and not seen_add(x)]
  556. else:
  557. return [x for x in seq
  558. if hashfunc(x) not in seen
  559. and not seen_add(hashfunc(x))]
  560. class UniqueAppender(object):
  561. """Appends items to a collection ensuring uniqueness.
  562. Additional appends() of the same object are ignored. Membership is
  563. determined by identity (``is a``) not equality (``==``).
  564. """
  565. def __init__(self, data, via=None):
  566. self.data = data
  567. self._unique = {}
  568. if via:
  569. self._data_appender = getattr(data, via)
  570. elif hasattr(data, 'append'):
  571. self._data_appender = data.append
  572. elif hasattr(data, 'add'):
  573. self._data_appender = data.add
  574. def append(self, item):
  575. id_ = id(item)
  576. if id_ not in self._unique:
  577. self._data_appender(item)
  578. self._unique[id_] = True
  579. def __iter__(self):
  580. return iter(self.data)
  581. def coerce_generator_arg(arg):
  582. if len(arg) == 1 and isinstance(arg[0], types.GeneratorType):
  583. return list(arg[0])
  584. else:
  585. return arg
  586. def to_list(x, default=None):
  587. if x is None:
  588. return default
  589. if not isinstance(x, collections.Iterable) or \
  590. isinstance(x, string_types + binary_types):
  591. return [x]
  592. elif isinstance(x, list):
  593. return x
  594. else:
  595. return list(x)
  596. def has_intersection(set_, iterable):
  597. """return True if any items of set_ are present in iterable.
  598. Goes through special effort to ensure __hash__ is not called
  599. on items in iterable that don't support it.
  600. """
  601. # TODO: optimize, write in C, etc.
  602. return bool(
  603. set_.intersection([i for i in iterable if i.__hash__])
  604. )
  605. def to_set(x):
  606. if x is None:
  607. return set()
  608. if not isinstance(x, set):
  609. return set(to_list(x))
  610. else:
  611. return x
  612. def to_column_set(x):
  613. if x is None:
  614. return column_set()
  615. if not isinstance(x, column_set):
  616. return column_set(to_list(x))
  617. else:
  618. return x
  619. def update_copy(d, _new=None, **kw):
  620. """Copy the given dict and update with the given values."""
  621. d = d.copy()
  622. if _new:
  623. d.update(_new)
  624. d.update(**kw)
  625. return d
  626. def flatten_iterator(x):
  627. """Given an iterator of which further sub-elements may also be
  628. iterators, flatten the sub-elements into a single iterator.
  629. """
  630. for elem in x:
  631. if not isinstance(elem, str) and hasattr(elem, '__iter__'):
  632. for y in flatten_iterator(elem):
  633. yield y
  634. else:
  635. yield elem
  636. class LRUCache(dict):
  637. """Dictionary with 'squishy' removal of least
  638. recently used items.
  639. Note that either get() or [] should be used here, but
  640. generally its not safe to do an "in" check first as the dictionary
  641. can change subsequent to that call.
  642. """
  643. def __init__(self, capacity=100, threshold=.5):
  644. self.capacity = capacity
  645. self.threshold = threshold
  646. self._counter = 0
  647. self._mutex = threading.Lock()
  648. def _inc_counter(self):
  649. self._counter += 1
  650. return self._counter
  651. def get(self, key, default=None):
  652. item = dict.get(self, key, default)
  653. if item is not default:
  654. item[2] = self._inc_counter()
  655. return item[1]
  656. else:
  657. return default
  658. def __getitem__(self, key):
  659. item = dict.__getitem__(self, key)
  660. item[2] = self._inc_counter()
  661. return item[1]
  662. def values(self):
  663. return [i[1] for i in dict.values(self)]
  664. def setdefault(self, key, value):
  665. if key in self:
  666. return self[key]
  667. else:
  668. self[key] = value
  669. return value
  670. def __setitem__(self, key, value):
  671. item = dict.get(self, key)
  672. if item is None:
  673. item = [key, value, self._inc_counter()]
  674. dict.__setitem__(self, key, item)
  675. else:
  676. item[1] = value
  677. self._manage_size()
  678. def _manage_size(self):
  679. if not self._mutex.acquire(False):
  680. return
  681. try:
  682. while len(self) > self.capacity + self.capacity * self.threshold:
  683. by_counter = sorted(dict.values(self),
  684. key=operator.itemgetter(2),
  685. reverse=True)
  686. for item in by_counter[self.capacity:]:
  687. try:
  688. del self[item[0]]
  689. except KeyError:
  690. # deleted elsewhere; skip
  691. continue
  692. finally:
  693. self._mutex.release()
  694. _lw_tuples = LRUCache(100)
  695. def lightweight_named_tuple(name, fields):
  696. hash_ = (name, ) + tuple(fields)
  697. tp_cls = _lw_tuples.get(hash_)
  698. if tp_cls:
  699. return tp_cls
  700. tp_cls = type(
  701. name, (_LW,),
  702. dict([
  703. (field, _property_getters[idx])
  704. for idx, field in enumerate(fields) if field is not None
  705. ] + [('__slots__', ())])
  706. )
  707. tp_cls._real_fields = fields
  708. tp_cls._fields = tuple([f for f in fields if f is not None])
  709. _lw_tuples[hash_] = tp_cls
  710. return tp_cls
  711. class ScopedRegistry(object):
  712. """A Registry that can store one or multiple instances of a single
  713. class on the basis of a "scope" function.
  714. The object implements ``__call__`` as the "getter", so by
  715. calling ``myregistry()`` the contained object is returned
  716. for the current scope.
  717. :param createfunc:
  718. a callable that returns a new object to be placed in the registry
  719. :param scopefunc:
  720. a callable that will return a key to store/retrieve an object.
  721. """
  722. def __init__(self, createfunc, scopefunc):
  723. """Construct a new :class:`.ScopedRegistry`.
  724. :param createfunc: A creation function that will generate
  725. a new value for the current scope, if none is present.
  726. :param scopefunc: A function that returns a hashable
  727. token representing the current scope (such as, current
  728. thread identifier).
  729. """
  730. self.createfunc = createfunc
  731. self.scopefunc = scopefunc
  732. self.registry = {}
  733. def __call__(self):
  734. key = self.scopefunc()
  735. try:
  736. return self.registry[key]
  737. except KeyError:
  738. return self.registry.setdefault(key, self.createfunc())
  739. def has(self):
  740. """Return True if an object is present in the current scope."""
  741. return self.scopefunc() in self.registry
  742. def set(self, obj):
  743. """Set the value for the current scope."""
  744. self.registry[self.scopefunc()] = obj
  745. def clear(self):
  746. """Clear the current scope, if any."""
  747. try:
  748. del self.registry[self.scopefunc()]
  749. except KeyError:
  750. pass
  751. class ThreadLocalRegistry(ScopedRegistry):
  752. """A :class:`.ScopedRegistry` that uses a ``threading.local()``
  753. variable for storage.
  754. """
  755. def __init__(self, createfunc):
  756. self.createfunc = createfunc
  757. self.registry = threading.local()
  758. def __call__(self):
  759. try:
  760. return self.registry.value
  761. except AttributeError:
  762. val = self.registry.value = self.createfunc()
  763. return val
  764. def has(self):
  765. return hasattr(self.registry, "value")
  766. def set(self, obj):
  767. self.registry.value = obj
  768. def clear(self):
  769. try:
  770. del self.registry.value
  771. except AttributeError:
  772. pass
  773. def _iter_id(iterable):
  774. """Generator: ((id(o), o) for o in iterable)."""
  775. for item in iterable:
  776. yield id(item), item