helpers.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  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. import abc
  8. import os
  9. try:
  10. from collections.abc import MutableMapping
  11. except ImportError:
  12. from collections import MutableMapping
  13. try:
  14. from collections import UserDict
  15. except ImportError:
  16. from UserDict import UserDict
  17. try:
  18. from collections import OrderedDict
  19. except ImportError:
  20. from ordereddict import OrderedDict
  21. try:
  22. import pathlib
  23. except ImportError:
  24. pathlib = None
  25. from io import open
  26. import sys
  27. try:
  28. from thread import get_ident
  29. except ImportError:
  30. try:
  31. from _thread import get_ident
  32. except ImportError:
  33. from _dummy_thread import get_ident
  34. __all__ = ['UserDict', 'OrderedDict', 'open']
  35. PY2 = sys.version_info[0] == 2
  36. PY3 = sys.version_info[0] == 3
  37. native_str = str
  38. str = type('str')
  39. def from_none(exc):
  40. """raise from_none(ValueError('a')) == raise ValueError('a') from None"""
  41. exc.__cause__ = None
  42. exc.__suppress_context__ = True
  43. return exc
  44. # from reprlib 3.2.1
  45. def recursive_repr(fillvalue='...'):
  46. 'Decorator to make a repr function return fillvalue for a recursive call'
  47. def decorating_function(user_function):
  48. repr_running = set()
  49. def wrapper(self):
  50. key = id(self), get_ident()
  51. if key in repr_running:
  52. return fillvalue
  53. repr_running.add(key)
  54. try:
  55. result = user_function(self)
  56. finally:
  57. repr_running.discard(key)
  58. return result
  59. # Can't use functools.wraps() here because of bootstrap issues
  60. wrapper.__module__ = getattr(user_function, '__module__')
  61. wrapper.__doc__ = getattr(user_function, '__doc__')
  62. wrapper.__name__ = getattr(user_function, '__name__')
  63. wrapper.__annotations__ = getattr(user_function, '__annotations__', {})
  64. return wrapper
  65. return decorating_function
  66. # from collections 3.2.1
  67. class _ChainMap(MutableMapping):
  68. ''' A ChainMap groups multiple dicts (or other mappings) together
  69. to create a single, updateable view.
  70. The underlying mappings are stored in a list. That list is public and can
  71. accessed or updated using the *maps* attribute. There is no other state.
  72. Lookups search the underlying mappings successively until a key is found.
  73. In contrast, writes, updates, and deletions only operate on the first
  74. mapping.
  75. '''
  76. def __init__(self, *maps):
  77. '''Initialize a ChainMap by setting *maps* to the given mappings.
  78. If no mappings are provided, a single empty dictionary is used.
  79. '''
  80. self.maps = list(maps) or [{}] # always at least one map
  81. def __missing__(self, key):
  82. raise KeyError(key)
  83. def __getitem__(self, key):
  84. for mapping in self.maps:
  85. try:
  86. # can't use 'key in mapping' with defaultdict
  87. return mapping[key]
  88. except KeyError:
  89. pass
  90. # support subclasses that define __missing__
  91. return self.__missing__(key)
  92. def get(self, key, default=None):
  93. return self[key] if key in self else default
  94. def __len__(self):
  95. # reuses stored hash values if possible
  96. return len(set().union(*self.maps))
  97. def __iter__(self):
  98. return iter(set().union(*self.maps))
  99. def __contains__(self, key):
  100. return any(key in m for m in self.maps)
  101. @recursive_repr()
  102. def __repr__(self):
  103. return '{0.__class__.__name__}({1})'.format(
  104. self, ', '.join(map(repr, self.maps)))
  105. @classmethod
  106. def fromkeys(cls, iterable, *args):
  107. 'Create a ChainMap with a single dict created from the iterable.'
  108. return cls(dict.fromkeys(iterable, *args))
  109. def copy(self):
  110. """
  111. New ChainMap or subclass with a new copy of
  112. maps[0] and refs to maps[1:]
  113. """
  114. return self.__class__(self.maps[0].copy(), *self.maps[1:])
  115. __copy__ = copy
  116. def new_child(self): # like Django's Context.push()
  117. 'New ChainMap with a new dict followed by all previous maps.'
  118. return self.__class__({}, *self.maps)
  119. @property
  120. def parents(self): # like Django's Context.pop()
  121. 'New ChainMap from maps[1:].'
  122. return self.__class__(*self.maps[1:])
  123. def __setitem__(self, key, value):
  124. self.maps[0][key] = value
  125. def __delitem__(self, key):
  126. try:
  127. del self.maps[0][key]
  128. except KeyError:
  129. raise KeyError(
  130. 'Key not found in the first mapping: {!r}'.format(key))
  131. def popitem(self):
  132. """
  133. Remove and return an item pair from maps[0].
  134. Raise KeyError is maps[0] is empty.
  135. """
  136. try:
  137. return self.maps[0].popitem()
  138. except KeyError:
  139. raise KeyError('No keys found in the first mapping.')
  140. def pop(self, key, *args):
  141. """
  142. Remove *key* from maps[0] and return its value.
  143. Raise KeyError if *key* not in maps[0].
  144. """
  145. try:
  146. return self.maps[0].pop(key, *args)
  147. except KeyError:
  148. raise KeyError(
  149. 'Key not found in the first mapping: {!r}'.format(key))
  150. def clear(self):
  151. 'Clear maps[0], leaving maps[1:] intact.'
  152. self.maps[0].clear()
  153. try:
  154. from collections import ChainMap
  155. except ImportError:
  156. ChainMap = _ChainMap
  157. _ABC = getattr(
  158. abc, 'ABC',
  159. # Python 3.3 compatibility
  160. abc.ABCMeta(
  161. native_str('__ABC'),
  162. (object,),
  163. dict(__metaclass__=abc.ABCMeta),
  164. ),
  165. )
  166. class _PathLike(_ABC):
  167. """Abstract base class for implementing the file system path protocol."""
  168. @abc.abstractmethod
  169. def __fspath__(self):
  170. """Return the file system path representation of the object."""
  171. raise NotImplementedError
  172. @classmethod
  173. def __subclasshook__(cls, subclass):
  174. return bool(
  175. hasattr(subclass, '__fspath__')
  176. # workaround for Python 3.5
  177. or pathlib and issubclass(subclass, pathlib.Path)
  178. )
  179. PathLike = getattr(os, 'PathLike', _PathLike)
  180. def _fspath(path):
  181. """Return the path representation of a path-like object.
  182. If str or bytes is passed in, it is returned unchanged. Otherwise the
  183. os.PathLike interface is used to get the path representation. If the
  184. path representation is not str or bytes, TypeError is raised. If the
  185. provided path is not str, bytes, or os.PathLike, TypeError is raised.
  186. """
  187. if isinstance(path, (str, bytes)):
  188. return path
  189. if not hasattr(path, '__fspath__') and isinstance(path, pathlib.Path):
  190. # workaround for Python 3.5
  191. return str(path)
  192. # Work from the object's type to match method resolution of other magic
  193. # methods.
  194. path_type = type(path)
  195. try:
  196. path_repr = path_type.__fspath__(path)
  197. except AttributeError:
  198. if hasattr(path_type, '__fspath__'):
  199. raise
  200. else:
  201. raise TypeError("expected str, bytes or os.PathLike object, "
  202. "not " + path_type.__name__)
  203. if isinstance(path_repr, (str, bytes)):
  204. return path_repr
  205. else:
  206. raise TypeError("expected {}.__fspath__() to return str or bytes, "
  207. "not {}".format(path_type.__name__,
  208. type(path_repr).__name__))
  209. fspath = getattr(os, 'fspath', _fspath)