datastructures.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.datastructures
  4. ~~~~~~~~~~~~~~~~~~~~~
  5. Custom types and data structures.
  6. """
  7. from __future__ import absolute_import, print_function, unicode_literals
  8. import sys
  9. import time
  10. from collections import (
  11. Callable, Mapping, MutableMapping, MutableSet, defaultdict,
  12. )
  13. from heapq import heapify, heappush, heappop
  14. from itertools import chain
  15. from billiard.einfo import ExceptionInfo # noqa
  16. from kombu.utils.encoding import safe_str, bytes_to_str
  17. from kombu.utils.limits import TokenBucket # noqa
  18. from celery.five import items, values
  19. from celery.utils.functional import LRUCache, first, uniq # noqa
  20. from celery.utils.text import match_case
  21. try:
  22. from django.utils.functional import LazyObject, LazySettings
  23. except ImportError:
  24. class LazyObject(object): # noqa
  25. pass
  26. LazySettings = LazyObject # noqa
  27. __all__ = ['GraphFormatter', 'CycleError', 'DependencyGraph',
  28. 'AttributeDictMixin', 'AttributeDict', 'DictAttribute',
  29. 'ConfigurationView', 'LimitedSet']
  30. DOT_HEAD = """
  31. {IN}{type} {id} {{
  32. {INp}graph [{attrs}]
  33. """
  34. DOT_ATTR = '{name}={value}'
  35. DOT_NODE = '{INp}"{0}" [{attrs}]'
  36. DOT_EDGE = '{INp}"{0}" {dir} "{1}" [{attrs}]'
  37. DOT_ATTRSEP = ', '
  38. DOT_DIRS = {'graph': '--', 'digraph': '->'}
  39. DOT_TAIL = '{IN}}}'
  40. REPR_LIMITED_SET = """\
  41. <{name}({size}): maxlen={0.maxlen}, expires={0.expires}, minlen={0.minlen}>\
  42. """
  43. sentinel = object()
  44. def force_mapping(m):
  45. if isinstance(m, (LazyObject, LazySettings)):
  46. m = m._wrapped
  47. return DictAttribute(m) if not isinstance(m, Mapping) else m
  48. class GraphFormatter(object):
  49. _attr = DOT_ATTR.strip()
  50. _node = DOT_NODE.strip()
  51. _edge = DOT_EDGE.strip()
  52. _head = DOT_HEAD.strip()
  53. _tail = DOT_TAIL.strip()
  54. _attrsep = DOT_ATTRSEP
  55. _dirs = dict(DOT_DIRS)
  56. scheme = {
  57. 'shape': 'box',
  58. 'arrowhead': 'vee',
  59. 'style': 'filled',
  60. 'fontname': 'HelveticaNeue',
  61. }
  62. edge_scheme = {
  63. 'color': 'darkseagreen4',
  64. 'arrowcolor': 'black',
  65. 'arrowsize': 0.7,
  66. }
  67. node_scheme = {'fillcolor': 'palegreen3', 'color': 'palegreen4'}
  68. term_scheme = {'fillcolor': 'palegreen1', 'color': 'palegreen2'}
  69. graph_scheme = {'bgcolor': 'mintcream'}
  70. def __init__(self, root=None, type=None, id=None,
  71. indent=0, inw=' ' * 4, **scheme):
  72. self.id = id or 'dependencies'
  73. self.root = root
  74. self.type = type or 'digraph'
  75. self.direction = self._dirs[self.type]
  76. self.IN = inw * (indent or 0)
  77. self.INp = self.IN + inw
  78. self.scheme = dict(self.scheme, **scheme)
  79. self.graph_scheme = dict(self.graph_scheme, root=self.label(self.root))
  80. def attr(self, name, value):
  81. value = '"{0}"'.format(value)
  82. return self.FMT(self._attr, name=name, value=value)
  83. def attrs(self, d, scheme=None):
  84. d = dict(self.scheme, **dict(scheme, **d or {}) if scheme else d)
  85. return self._attrsep.join(
  86. safe_str(self.attr(k, v)) for k, v in items(d)
  87. )
  88. def head(self, **attrs):
  89. return self.FMT(
  90. self._head, id=self.id, type=self.type,
  91. attrs=self.attrs(attrs, self.graph_scheme),
  92. )
  93. def tail(self):
  94. return self.FMT(self._tail)
  95. def label(self, obj):
  96. return obj
  97. def node(self, obj, **attrs):
  98. return self.draw_node(obj, self.node_scheme, attrs)
  99. def terminal_node(self, obj, **attrs):
  100. return self.draw_node(obj, self.term_scheme, attrs)
  101. def edge(self, a, b, **attrs):
  102. return self.draw_edge(a, b, **attrs)
  103. def _enc(self, s):
  104. return s.encode('utf-8', 'ignore')
  105. def FMT(self, fmt, *args, **kwargs):
  106. return self._enc(fmt.format(
  107. *args, **dict(kwargs, IN=self.IN, INp=self.INp)
  108. ))
  109. def draw_edge(self, a, b, scheme=None, attrs=None):
  110. return self.FMT(
  111. self._edge, self.label(a), self.label(b),
  112. dir=self.direction, attrs=self.attrs(attrs, self.edge_scheme),
  113. )
  114. def draw_node(self, obj, scheme=None, attrs=None):
  115. return self.FMT(
  116. self._node, self.label(obj), attrs=self.attrs(attrs, scheme),
  117. )
  118. class CycleError(Exception):
  119. """A cycle was detected in an acyclic graph."""
  120. class DependencyGraph(object):
  121. """A directed acyclic graph of objects and their dependencies.
  122. Supports a robust topological sort
  123. to detect the order in which they must be handled.
  124. Takes an optional iterator of ``(obj, dependencies)``
  125. tuples to build the graph from.
  126. .. warning::
  127. Does not support cycle detection.
  128. """
  129. def __init__(self, it=None, formatter=None):
  130. self.formatter = formatter or GraphFormatter()
  131. self.adjacent = {}
  132. if it is not None:
  133. self.update(it)
  134. def add_arc(self, obj):
  135. """Add an object to the graph."""
  136. self.adjacent.setdefault(obj, [])
  137. def add_edge(self, A, B):
  138. """Add an edge from object ``A`` to object ``B``
  139. (``A`` depends on ``B``)."""
  140. self[A].append(B)
  141. def connect(self, graph):
  142. """Add nodes from another graph."""
  143. self.adjacent.update(graph.adjacent)
  144. def topsort(self):
  145. """Sort the graph topologically.
  146. :returns: a list of objects in the order
  147. in which they must be handled.
  148. """
  149. graph = DependencyGraph()
  150. components = self._tarjan72()
  151. NC = {
  152. node: component for component in components for node in component
  153. }
  154. for component in components:
  155. graph.add_arc(component)
  156. for node in self:
  157. node_c = NC[node]
  158. for successor in self[node]:
  159. successor_c = NC[successor]
  160. if node_c != successor_c:
  161. graph.add_edge(node_c, successor_c)
  162. return [t[0] for t in graph._khan62()]
  163. def valency_of(self, obj):
  164. """Return the valency (degree) of a vertex in the graph."""
  165. try:
  166. l = [len(self[obj])]
  167. except KeyError:
  168. return 0
  169. for node in self[obj]:
  170. l.append(self.valency_of(node))
  171. return sum(l)
  172. def update(self, it):
  173. """Update the graph with data from a list
  174. of ``(obj, dependencies)`` tuples."""
  175. tups = list(it)
  176. for obj, _ in tups:
  177. self.add_arc(obj)
  178. for obj, deps in tups:
  179. for dep in deps:
  180. self.add_edge(obj, dep)
  181. def edges(self):
  182. """Return generator that yields for all edges in the graph."""
  183. return (obj for obj, adj in items(self) if adj)
  184. def _khan62(self):
  185. """Khans simple topological sort algorithm from '62
  186. See http://en.wikipedia.org/wiki/Topological_sorting
  187. """
  188. count = defaultdict(lambda: 0)
  189. result = []
  190. for node in self:
  191. for successor in self[node]:
  192. count[successor] += 1
  193. ready = [node for node in self if not count[node]]
  194. while ready:
  195. node = ready.pop()
  196. result.append(node)
  197. for successor in self[node]:
  198. count[successor] -= 1
  199. if count[successor] == 0:
  200. ready.append(successor)
  201. result.reverse()
  202. return result
  203. def _tarjan72(self):
  204. """Tarjan's algorithm to find strongly connected components.
  205. See http://bit.ly/vIMv3h.
  206. """
  207. result, stack, low = [], [], {}
  208. def visit(node):
  209. if node in low:
  210. return
  211. num = len(low)
  212. low[node] = num
  213. stack_pos = len(stack)
  214. stack.append(node)
  215. for successor in self[node]:
  216. visit(successor)
  217. low[node] = min(low[node], low[successor])
  218. if num == low[node]:
  219. component = tuple(stack[stack_pos:])
  220. stack[stack_pos:] = []
  221. result.append(component)
  222. for item in component:
  223. low[item] = len(self)
  224. for node in self:
  225. visit(node)
  226. return result
  227. def to_dot(self, fh, formatter=None):
  228. """Convert the graph to DOT format.
  229. :param fh: A file, or a file-like object to write the graph to.
  230. """
  231. seen = set()
  232. draw = formatter or self.formatter
  233. def P(s):
  234. print(bytes_to_str(s), file=fh)
  235. def if_not_seen(fun, obj):
  236. if draw.label(obj) not in seen:
  237. P(fun(obj))
  238. seen.add(draw.label(obj))
  239. P(draw.head())
  240. for obj, adjacent in items(self):
  241. if not adjacent:
  242. if_not_seen(draw.terminal_node, obj)
  243. for req in adjacent:
  244. if_not_seen(draw.node, obj)
  245. P(draw.edge(obj, req))
  246. P(draw.tail())
  247. def format(self, obj):
  248. return self.formatter(obj) if self.formatter else obj
  249. def __iter__(self):
  250. return iter(self.adjacent)
  251. def __getitem__(self, node):
  252. return self.adjacent[node]
  253. def __len__(self):
  254. return len(self.adjacent)
  255. def __contains__(self, obj):
  256. return obj in self.adjacent
  257. def _iterate_items(self):
  258. return items(self.adjacent)
  259. items = iteritems = _iterate_items
  260. def __repr__(self):
  261. return '\n'.join(self.repr_node(N) for N in self)
  262. def repr_node(self, obj, level=1, fmt='{0}({1})'):
  263. output = [fmt.format(obj, self.valency_of(obj))]
  264. if obj in self:
  265. for other in self[obj]:
  266. d = fmt.format(other, self.valency_of(other))
  267. output.append(' ' * level + d)
  268. output.extend(self.repr_node(other, level + 1).split('\n')[1:])
  269. return '\n'.join(output)
  270. class AttributeDictMixin(object):
  271. """Augment classes with a Mapping interface by adding attribute access.
  272. I.e. `d.key -> d[key]`.
  273. """
  274. def __getattr__(self, k):
  275. """`d.key -> d[key]`"""
  276. try:
  277. return self[k]
  278. except KeyError:
  279. raise AttributeError(
  280. '{0!r} object has no attribute {1!r}'.format(
  281. type(self).__name__, k))
  282. def __setattr__(self, key, value):
  283. """`d[key] = value -> d.key = value`"""
  284. self[key] = value
  285. class AttributeDict(dict, AttributeDictMixin):
  286. """Dict subclass with attribute access."""
  287. pass
  288. class DictAttribute(object):
  289. """Dict interface to attributes.
  290. `obj[k] -> obj.k`
  291. `obj[k] = val -> obj.k = val`
  292. """
  293. obj = None
  294. def __init__(self, obj):
  295. object.__setattr__(self, 'obj', obj)
  296. def __getattr__(self, key):
  297. return getattr(self.obj, key)
  298. def __setattr__(self, key, value):
  299. return setattr(self.obj, key, value)
  300. def get(self, key, default=None):
  301. try:
  302. return self[key]
  303. except KeyError:
  304. return default
  305. def setdefault(self, key, default):
  306. if key not in self:
  307. self[key] = default
  308. def __getitem__(self, key):
  309. try:
  310. return getattr(self.obj, key)
  311. except AttributeError:
  312. raise KeyError(key)
  313. def __setitem__(self, key, value):
  314. setattr(self.obj, key, value)
  315. def __contains__(self, key):
  316. return hasattr(self.obj, key)
  317. def _iterate_keys(self):
  318. return iter(dir(self.obj))
  319. iterkeys = _iterate_keys
  320. def __iter__(self):
  321. return self._iterate_keys()
  322. def _iterate_items(self):
  323. for key in self._iterate_keys():
  324. yield key, getattr(self.obj, key)
  325. iteritems = _iterate_items
  326. def _iterate_values(self):
  327. for key in self._iterate_keys():
  328. yield getattr(self.obj, key)
  329. itervalues = _iterate_values
  330. if sys.version_info[0] == 3: # pragma: no cover
  331. items = _iterate_items
  332. keys = _iterate_keys
  333. values = _iterate_values
  334. else:
  335. def keys(self):
  336. return list(self)
  337. def items(self):
  338. return list(self._iterate_items())
  339. def values(self):
  340. return list(self._iterate_values())
  341. MutableMapping.register(DictAttribute)
  342. class ConfigurationView(AttributeDictMixin):
  343. """A view over an applications configuration dicts.
  344. Custom (but older) version of :class:`collections.ChainMap`.
  345. If the key does not exist in ``changes``, the ``defaults`` dicts
  346. are consulted.
  347. :param changes: Dict containing changes to the configuration.
  348. :param defaults: List of dicts containing the default configuration.
  349. """
  350. key_t = None
  351. changes = None
  352. defaults = None
  353. _order = None
  354. def __init__(self, changes, defaults=None, key_t=None, prefix=None):
  355. defaults = [] if defaults is None else defaults
  356. self.__dict__.update(
  357. changes=changes,
  358. defaults=defaults,
  359. key_t=key_t,
  360. _order=[changes] + defaults,
  361. prefix=prefix.rstrip('_') + '_' if prefix else prefix,
  362. )
  363. def _to_keys(self, key):
  364. prefix = self.prefix
  365. if prefix:
  366. pkey = prefix + key if not key.startswith(prefix) else key
  367. return match_case(pkey, prefix), self._key(key)
  368. return self._key(key),
  369. def _key(self, key):
  370. return self.key_t(key) if self.key_t is not None else key
  371. def add_defaults(self, d):
  372. d = force_mapping(d)
  373. self.defaults.insert(0, d)
  374. self._order.insert(1, d)
  375. def __getitem__(self, key):
  376. keys = self._to_keys(key)
  377. for k in keys:
  378. for d in self._order:
  379. try:
  380. return d[k]
  381. except KeyError:
  382. pass
  383. if len(keys) > 1:
  384. raise KeyError(
  385. 'Key not found: {0!r} (with prefix: {0!r})'.format(*keys))
  386. raise KeyError(key)
  387. def __setitem__(self, key, value):
  388. self.changes[self._key(key)] = value
  389. def first(self, *keys):
  390. return first(None, (self.get(key) for key in keys))
  391. def get(self, key, default=None):
  392. try:
  393. return self[key]
  394. except KeyError:
  395. return default
  396. def clear(self):
  397. """Remove all changes, but keep defaults."""
  398. self.changes.clear()
  399. def setdefault(self, key, default):
  400. key = self._key(key)
  401. if key not in self:
  402. self[key] = default
  403. def update(self, *args, **kwargs):
  404. return self.changes.update(*args, **kwargs)
  405. def __contains__(self, key):
  406. keys = self._to_keys(key)
  407. return any(any(k in m for k in keys) for m in self._order)
  408. def __bool__(self):
  409. return any(self._order)
  410. __nonzero__ = __bool__ # Py2
  411. def __repr__(self):
  412. return repr(dict(items(self)))
  413. def __iter__(self):
  414. return self._iterate_keys()
  415. def __len__(self):
  416. # The logic for iterating keys includes uniq(),
  417. # so to be safe we count by explicitly iterating
  418. return len(set().union(*self._order))
  419. def _iter(self, op):
  420. # defaults must be first in the stream, so values in
  421. # changes takes precedence.
  422. return chain(*[op(d) for d in reversed(self._order)])
  423. def swap_with(self, other):
  424. changes = other.__dict__['changes']
  425. defaults = other.__dict__['defaults']
  426. self.__dict__.update(
  427. changes=changes,
  428. defaults=defaults,
  429. key_t=other.__dict__['key_t'],
  430. prefix=other.__dict__['prefix'],
  431. _order=[changes] + defaults
  432. )
  433. def _iterate_keys(self):
  434. return uniq(self._iter(lambda d: d.keys()))
  435. iterkeys = _iterate_keys
  436. def _iterate_items(self):
  437. return ((key, self[key]) for key in self)
  438. iteritems = _iterate_items
  439. def _iterate_values(self):
  440. return (self[key] for key in self)
  441. itervalues = _iterate_values
  442. if sys.version_info[0] == 3: # pragma: no cover
  443. keys = _iterate_keys
  444. items = _iterate_items
  445. values = _iterate_values
  446. else: # noqa
  447. def keys(self):
  448. return list(self._iterate_keys())
  449. def items(self):
  450. return list(self._iterate_items())
  451. def values(self):
  452. return list(self._iterate_values())
  453. MutableMapping.register(ConfigurationView)
  454. class LimitedSet(object):
  455. """Kind-of Set (or priority queue) with limitations.
  456. Good for when you need to test for membership (`a in set`),
  457. but the set should not grow unbounded.
  458. Maxlen is enforced at all times, so if the limit is reached
  459. we will also remove non-expired items.
  460. You can also configure minlen, which is the minimal residual size
  461. of the set.
  462. All arguments are optional, and no limits are enabled by default.
  463. :keyword maxlen: Optional max number of items.
  464. Adding more items than maxlen will result in immediate
  465. removal of items sorted by oldest insertion time.
  466. :keyword expires: TTL for all items.
  467. Expired items are purged as keys are inserted.
  468. :keyword minlen: Minimal residual size of this set.
  469. .. versionadded:: 4.0
  470. Value must be less than ``maxlen`` if both are configured.
  471. Older expired items will be deleted, only after the set
  472. exceeds minlen number of items.
  473. :keyword data: Initial data to initialize set with.
  474. Can be an iterable of ``(key, value)`` pairs,
  475. a dict (``{key: insertion_time}``), or another instance
  476. of :class:`LimitedSet`.
  477. Example::
  478. >>> s = LimitedSet(maxlen=50000, expires=3600, minlen=4000)
  479. >>> for i in range(60000):
  480. ... s.add(i)
  481. ... s.add(str(i))
  482. ...
  483. >>> 57000 in s # last 50k inserted values are kept
  484. True
  485. >>> '10' in s # '10' did expire and was purged from set.
  486. False
  487. >>> len(s) # maxlen is reached
  488. 50000
  489. >>> s.purge(now=time.time() + 7200) # clock + 2 hours
  490. >>> len(s) # now only minlen items are cached
  491. 4000
  492. >>>> 57000 in s # even this item is gone now
  493. False
  494. """
  495. max_heap_percent_overload = 15
  496. def __init__(self, maxlen=0, expires=0, data=None, minlen=0):
  497. self.maxlen = 0 if maxlen is None else maxlen
  498. self.minlen = 0 if minlen is None else minlen
  499. self.expires = 0 if expires is None else expires
  500. self._data = {}
  501. self._heap = []
  502. # make shortcuts
  503. self.__len__ = self._data.__len__
  504. self.__contains__ = self._data.__contains__
  505. if data:
  506. # import items from data
  507. self.update(data)
  508. if not self.maxlen >= self.minlen >= 0:
  509. raise ValueError(
  510. 'minlen must be a positive number, less or equal to maxlen.')
  511. if self.expires < 0:
  512. raise ValueError('expires cannot be negative!')
  513. def _refresh_heap(self):
  514. """Time consuming recreating of heap. Do not run this too often."""
  515. self._heap[:] = [
  516. entry for entry in values(self._data)
  517. if entry is not sentinel
  518. ]
  519. heapify(self._heap)
  520. def clear(self):
  521. """Clear all data, start from scratch again."""
  522. self._data.clear()
  523. self._heap[:] = []
  524. def add(self, item, now=None):
  525. """Add a new item, or reset the expiry time of an existing item."""
  526. now = now or time.time()
  527. if item in self._data:
  528. self.discard(item)
  529. entry = (now, item)
  530. self._data[item] = entry
  531. heappush(self._heap, entry)
  532. if self.maxlen and len(self._data) >= self.maxlen:
  533. self.purge()
  534. def update(self, other):
  535. """Update this set from other LimitedSet, dict or iterable."""
  536. if not other:
  537. return
  538. if isinstance(other, LimitedSet):
  539. self._data.update(other._data)
  540. self._refresh_heap()
  541. self.purge()
  542. elif isinstance(other, dict):
  543. # revokes are sent as a dict
  544. for key, inserted in items(other):
  545. if isinstance(inserted, (tuple, list)):
  546. # in case someone uses ._data directly for sending update
  547. inserted = inserted[0]
  548. if not isinstance(inserted, float):
  549. raise ValueError(
  550. 'Expecting float timestamp, got type '
  551. '{0!r} with value: {1}'.format(
  552. type(inserted), inserted))
  553. self.add(key, inserted)
  554. else:
  555. # XXX AVOID THIS, it could keep old data if more parties
  556. # exchange them all over and over again
  557. for obj in other:
  558. self.add(obj)
  559. def discard(self, item):
  560. # mark an existing item as removed. If KeyError is not found, pass.
  561. entry = self._data.pop(item, sentinel)
  562. if entry is not sentinel:
  563. if self._heap_overload > self.max_heap_percent_overload:
  564. self._refresh_heap()
  565. pop_value = discard
  566. def purge(self, now=None):
  567. """Check oldest items and remove them if needed.
  568. :keyword now: Time of purging -- by default right now.
  569. This can be useful for unit testing.
  570. """
  571. now = now or time.time()
  572. now = now() if isinstance(now, Callable) else now
  573. if self.maxlen:
  574. while len(self._data) > self.maxlen:
  575. self.pop()
  576. # time based expiring:
  577. if self.expires:
  578. while len(self._data) > self.minlen >= 0:
  579. inserted_time, _ = self._heap[0]
  580. if inserted_time + self.expires > now:
  581. break # oldest item has not expired yet
  582. self.pop()
  583. def pop(self, default=None):
  584. """Remove and return the oldest item, or :const:`None` when empty."""
  585. while self._heap:
  586. _, item = heappop(self._heap)
  587. if self._data.pop(item, None) is not sentinel:
  588. return item
  589. return default
  590. def as_dict(self):
  591. """Whole set as serializable dictionary.
  592. Example::
  593. >>> s = LimitedSet(maxlen=200)
  594. >>> r = LimitedSet(maxlen=200)
  595. >>> for i in range(500):
  596. ... s.add(i)
  597. ...
  598. >>> r.update(s.as_dict())
  599. >>> r == s
  600. True
  601. """
  602. return {key: inserted for inserted, key in values(self._data)}
  603. def __eq__(self, other):
  604. return self._data == other._data
  605. def __ne__(self, other):
  606. return not self.__eq__(other)
  607. def __repr__(self):
  608. return REPR_LIMITED_SET.format(
  609. self, name=type(self).__name__, size=len(self),
  610. )
  611. def __iter__(self):
  612. return (i for _, i in sorted(values(self._data)))
  613. def __len__(self):
  614. return len(self._data)
  615. def __contains__(self, key):
  616. return key in self._data
  617. def __reduce__(self):
  618. return self.__class__, (
  619. self.maxlen, self.expires, self.as_dict(), self.minlen)
  620. def __bool__(self):
  621. return bool(self._data)
  622. __nonzero__ = __bool__ # Py2
  623. @property
  624. def _heap_overload(self):
  625. """Compute how much is heap bigger than data [percents]."""
  626. if not self._data:
  627. return len(self._heap)
  628. return len(self._heap) * 100 / len(self._data) - 100
  629. MutableSet.register(LimitedSet)