singledispatch_helpers.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. from __future__ import absolute_import
  4. from __future__ import division
  5. from __future__ import print_function
  6. from __future__ import unicode_literals
  7. from abc import ABCMeta
  8. from collections import MutableMapping
  9. import sys
  10. try:
  11. from collections import UserDict
  12. except ImportError:
  13. from UserDict import UserDict
  14. try:
  15. from collections import OrderedDict
  16. except ImportError:
  17. from ordereddict import OrderedDict
  18. try:
  19. from thread import get_ident
  20. except ImportError:
  21. try:
  22. from _thread import get_ident
  23. except ImportError:
  24. from _dummy_thread import get_ident
  25. def recursive_repr(fillvalue='...'):
  26. 'Decorator to make a repr function return fillvalue for a recursive call'
  27. def decorating_function(user_function):
  28. repr_running = set()
  29. def wrapper(self):
  30. key = id(self), get_ident()
  31. if key in repr_running:
  32. return fillvalue
  33. repr_running.add(key)
  34. try:
  35. result = user_function(self)
  36. finally:
  37. repr_running.discard(key)
  38. return result
  39. # Can't use functools.wraps() here because of bootstrap issues
  40. wrapper.__module__ = getattr(user_function, '__module__')
  41. wrapper.__doc__ = getattr(user_function, '__doc__')
  42. wrapper.__name__ = getattr(user_function, '__name__')
  43. wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
  44. return wrapper
  45. return decorating_function
  46. class ChainMap(MutableMapping):
  47. ''' A ChainMap groups multiple dicts (or other mappings) together
  48. to create a single, updateable view.
  49. The underlying mappings are stored in a list. That list is public and can
  50. accessed or updated using the *maps* attribute. There is no other state.
  51. Lookups search the underlying mappings successively until a key is found.
  52. In contrast, writes, updates, and deletions only operate on the first
  53. mapping.
  54. '''
  55. def __init__(self, *maps):
  56. '''Initialize a ChainMap by setting *maps* to the given mappings.
  57. If no mappings are provided, a single empty dictionary is used.
  58. '''
  59. self.maps = list(maps) or [{}] # always at least one map
  60. def __missing__(self, key):
  61. raise KeyError(key)
  62. def __getitem__(self, key):
  63. for mapping in self.maps:
  64. try:
  65. return mapping[key] # can't use 'key in mapping' with defaultdict
  66. except KeyError:
  67. pass
  68. return self.__missing__(key) # support subclasses that define __missing__
  69. def get(self, key, default=None):
  70. return self[key] if key in self else default
  71. def __len__(self):
  72. return len(set().union(*self.maps)) # reuses stored hash values if possible
  73. def __iter__(self):
  74. return iter(set().union(*self.maps))
  75. def __contains__(self, key):
  76. return any(key in m for m in self.maps)
  77. @recursive_repr()
  78. def __repr__(self):
  79. return '{0.__class__.__name__}({1})'.format(
  80. self, ', '.join(map(repr, self.maps)))
  81. @classmethod
  82. def fromkeys(cls, iterable, *args):
  83. 'Create a ChainMap with a single dict created from the iterable.'
  84. return cls(dict.fromkeys(iterable, *args))
  85. def copy(self):
  86. 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]'
  87. return self.__class__(self.maps[0].copy(), *self.maps[1:])
  88. __copy__ = copy
  89. def new_child(self): # like Django's Context.push()
  90. 'New ChainMap with a new dict followed by all previous maps.'
  91. return self.__class__({}, *self.maps)
  92. @property
  93. def parents(self): # like Django's Context.pop()
  94. 'New ChainMap from maps[1:].'
  95. return self.__class__(*self.maps[1:])
  96. def __setitem__(self, key, value):
  97. self.maps[0][key] = value
  98. def __delitem__(self, key):
  99. try:
  100. del self.maps[0][key]
  101. except KeyError:
  102. raise KeyError('Key not found in the first mapping: {!r}'.format(key))
  103. def popitem(self):
  104. 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.'
  105. try:
  106. return self.maps[0].popitem()
  107. except KeyError:
  108. raise KeyError('No keys found in the first mapping.')
  109. def pop(self, key, *args):
  110. 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].'
  111. try:
  112. return self.maps[0].pop(key, *args)
  113. except KeyError:
  114. raise KeyError('Key not found in the first mapping: {!r}'.format(key))
  115. def clear(self):
  116. 'Clear maps[0], leaving maps[1:] intact.'
  117. self.maps[0].clear()
  118. class MappingProxyType(UserDict):
  119. def __init__(self, data):
  120. UserDict.__init__(self)
  121. self.data = data
  122. def get_cache_token():
  123. return ABCMeta._abc_invalidation_counter
  124. class Support(object):
  125. def dummy(self):
  126. pass
  127. def cpython_only(self, func):
  128. if 'PyPy' in sys.version:
  129. return self.dummy
  130. return func