datastructures.py 23 KB

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