utils.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. """
  2. Utility functions.
  3. """
  4. from contextlib import contextmanager
  5. import warnings
  6. # Python 2/3 independant dict iteration
  7. iteritems = getattr(dict, 'iteritems', dict.items)
  8. itervalues = getattr(dict, 'itervalues', dict.values)
  9. class LRUCache(dict):
  10. """
  11. A simple LRU cache.
  12. """
  13. def __init__(self, *args, **kwargs):
  14. """
  15. :param capacity: How many items to store before cleaning up old items
  16. or ``None`` for an unlimited cache size
  17. """
  18. self.capacity = kwargs.pop('capacity', None)
  19. if self.capacity is None:
  20. self.capacity = float('nan')
  21. self.lru = []
  22. super(LRUCache, self).__init__(*args, **kwargs)
  23. def refresh(self, key):
  24. """
  25. Push a key to the tail of the LRU queue
  26. """
  27. if key in self.lru:
  28. self.lru.remove(key)
  29. self.lru.append(key)
  30. def get(self, key, default=None):
  31. item = super(LRUCache, self).get(key, default)
  32. self.refresh(key)
  33. return item
  34. def __getitem__(self, key):
  35. item = super(LRUCache, self).__getitem__(key)
  36. self.refresh(key)
  37. return item
  38. def __setitem__(self, key, value):
  39. super(LRUCache, self).__setitem__(key, value)
  40. self.refresh(key)
  41. # Check, if the cache is full and we have to remove old items
  42. # If the queue is of unlimited size, self.capacity is NaN and
  43. # x > NaN is always False in Python and the cache won't be cleared.
  44. if len(self) > self.capacity:
  45. self.pop(self.lru.pop(0))
  46. def __delitem__(self, key):
  47. super(LRUCache, self).__delitem__(key)
  48. self.lru.remove(key)
  49. def clear(self):
  50. super(LRUCache, self).clear()
  51. del self.lru[:]
  52. # Source: https://github.com/PythonCharmers/python-future/blob/466bfb2dfa36d865285dc31fe2b0c0a53ff0f181/future/utils/__init__.py#L102-L134
  53. def with_metaclass(meta, *bases):
  54. """
  55. Function from jinja2/_compat.py. License: BSD.
  56. Use it like this::
  57. class BaseForm(object):
  58. pass
  59. class FormType(type):
  60. pass
  61. class Form(with_metaclass(FormType, BaseForm)):
  62. pass
  63. This requires a bit of explanation: the basic idea is to make a
  64. dummy metaclass for one level of class instantiation that replaces
  65. itself with the actual metaclass. Because of internal type checks
  66. we also need to make sure that we downgrade the custom metaclass
  67. for one level to something closer to type (that's why __call__ and
  68. __init__ comes back from type etc.).
  69. This has the advantage over six.with_metaclass of not introducing
  70. dummy classes into the final MRO.
  71. """
  72. class Metaclass(meta):
  73. __call__ = type.__call__
  74. __init__ = type.__init__
  75. def __new__(cls, name, this_bases, d):
  76. if this_bases is None:
  77. return type.__new__(cls, name, (), d)
  78. return meta(name, bases, d)
  79. return Metaclass('temporary_class', None, {})
  80. @contextmanager
  81. def catch_warning(warning_cls):
  82. with warnings.catch_warnings():
  83. warnings.filterwarnings('error', category=warning_cls)
  84. yield
  85. class FrozenDict(dict):
  86. def __hash__(self):
  87. return hash(tuple(sorted(self.items())))
  88. def _immutable(self, *args, **kws):
  89. raise TypeError('object is immutable')
  90. __setitem__ = _immutable
  91. __delitem__ = _immutable
  92. clear = _immutable
  93. update = _immutable
  94. setdefault = _immutable
  95. pop = _immutable
  96. popitem = _immutable
  97. def freeze(obj):
  98. if isinstance(obj, dict):
  99. return FrozenDict((k, freeze(v)) for k, v in obj.items())
  100. elif isinstance(obj, list):
  101. return tuple(freeze(el) for el in obj)
  102. elif isinstance(obj, set):
  103. return frozenset(obj)
  104. else:
  105. return obj