_compat.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. from __future__ import absolute_import, division, print_function
  2. import platform
  3. import sys
  4. import types
  5. import warnings
  6. PY2 = sys.version_info[0] == 2
  7. PYPY = platform.python_implementation() == "PyPy"
  8. if PYPY or sys.version_info[:2] >= (3, 6):
  9. ordered_dict = dict
  10. else:
  11. from collections import OrderedDict
  12. ordered_dict = OrderedDict
  13. if PY2:
  14. from collections import Mapping, Sequence
  15. from UserDict import IterableUserDict
  16. # We 'bundle' isclass instead of using inspect as importing inspect is
  17. # fairly expensive (order of 10-15 ms for a modern machine in 2016)
  18. def isclass(klass):
  19. return isinstance(klass, (type, types.ClassType))
  20. def new_class(name, bases, kwds, exec_body):
  21. """
  22. A minimal stub of types.new_class that we need for make_class.
  23. """
  24. ns = {}
  25. exec_body(ns)
  26. return type(name, bases, ns)
  27. # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
  28. TYPE = "type"
  29. def iteritems(d):
  30. return d.iteritems()
  31. # Python 2 is bereft of a read-only dict proxy, so we make one!
  32. class ReadOnlyDict(IterableUserDict):
  33. """
  34. Best-effort read-only dict wrapper.
  35. """
  36. def __setitem__(self, key, val):
  37. # We gently pretend we're a Python 3 mappingproxy.
  38. raise TypeError(
  39. "'mappingproxy' object does not support item assignment"
  40. )
  41. def update(self, _):
  42. # We gently pretend we're a Python 3 mappingproxy.
  43. raise AttributeError(
  44. "'mappingproxy' object has no attribute 'update'"
  45. )
  46. def __delitem__(self, _):
  47. # We gently pretend we're a Python 3 mappingproxy.
  48. raise TypeError(
  49. "'mappingproxy' object does not support item deletion"
  50. )
  51. def clear(self):
  52. # We gently pretend we're a Python 3 mappingproxy.
  53. raise AttributeError(
  54. "'mappingproxy' object has no attribute 'clear'"
  55. )
  56. def pop(self, key, default=None):
  57. # We gently pretend we're a Python 3 mappingproxy.
  58. raise AttributeError(
  59. "'mappingproxy' object has no attribute 'pop'"
  60. )
  61. def popitem(self):
  62. # We gently pretend we're a Python 3 mappingproxy.
  63. raise AttributeError(
  64. "'mappingproxy' object has no attribute 'popitem'"
  65. )
  66. def setdefault(self, key, default=None):
  67. # We gently pretend we're a Python 3 mappingproxy.
  68. raise AttributeError(
  69. "'mappingproxy' object has no attribute 'setdefault'"
  70. )
  71. def __repr__(self):
  72. # Override to be identical to the Python 3 version.
  73. return "mappingproxy(" + repr(self.data) + ")"
  74. def metadata_proxy(d):
  75. res = ReadOnlyDict()
  76. res.data.update(d) # We blocked update, so we have to do it like this.
  77. return res
  78. def just_warn(*args, **kw): # pragma: no cover
  79. """
  80. We only warn on Python 3 because we are not aware of any concrete
  81. consequences of not setting the cell on Python 2.
  82. """
  83. else: # Python 3 and later.
  84. from collections.abc import Mapping, Sequence # noqa
  85. def just_warn(*args, **kw):
  86. """
  87. We only warn on Python 3 because we are not aware of any concrete
  88. consequences of not setting the cell on Python 2.
  89. """
  90. warnings.warn(
  91. "Running interpreter doesn't sufficiently support code object "
  92. "introspection. Some features like bare super() or accessing "
  93. "__class__ will not work with slotted classes.",
  94. RuntimeWarning,
  95. stacklevel=2,
  96. )
  97. def isclass(klass):
  98. return isinstance(klass, type)
  99. TYPE = "class"
  100. def iteritems(d):
  101. return d.items()
  102. new_class = types.new_class
  103. def metadata_proxy(d):
  104. return types.MappingProxyType(dict(d))
  105. def make_set_closure_cell():
  106. """Return a function of two arguments (cell, value) which sets
  107. the value stored in the closure cell `cell` to `value`.
  108. """
  109. # pypy makes this easy. (It also supports the logic below, but
  110. # why not do the easy/fast thing?)
  111. if PYPY:
  112. def set_closure_cell(cell, value):
  113. cell.__setstate__((value,))
  114. return set_closure_cell
  115. # Otherwise gotta do it the hard way.
  116. # Create a function that will set its first cellvar to `value`.
  117. def set_first_cellvar_to(value):
  118. x = value
  119. return
  120. # This function will be eliminated as dead code, but
  121. # not before its reference to `x` forces `x` to be
  122. # represented as a closure cell rather than a local.
  123. def force_x_to_be_a_cell(): # pragma: no cover
  124. return x
  125. try:
  126. # Extract the code object and make sure our assumptions about
  127. # the closure behavior are correct.
  128. if PY2:
  129. co = set_first_cellvar_to.func_code
  130. else:
  131. co = set_first_cellvar_to.__code__
  132. if co.co_cellvars != ("x",) or co.co_freevars != ():
  133. raise AssertionError # pragma: no cover
  134. # Convert this code object to a code object that sets the
  135. # function's first _freevar_ (not cellvar) to the argument.
  136. if sys.version_info >= (3, 8):
  137. # CPython 3.8+ has an incompatible CodeType signature
  138. # (added a posonlyargcount argument) but also added
  139. # CodeType.replace() to do this without counting parameters.
  140. set_first_freevar_code = co.replace(
  141. co_cellvars=co.co_freevars, co_freevars=co.co_cellvars
  142. )
  143. else:
  144. args = [co.co_argcount]
  145. if not PY2:
  146. args.append(co.co_kwonlyargcount)
  147. args.extend(
  148. [
  149. co.co_nlocals,
  150. co.co_stacksize,
  151. co.co_flags,
  152. co.co_code,
  153. co.co_consts,
  154. co.co_names,
  155. co.co_varnames,
  156. co.co_filename,
  157. co.co_name,
  158. co.co_firstlineno,
  159. co.co_lnotab,
  160. # These two arguments are reversed:
  161. co.co_cellvars,
  162. co.co_freevars,
  163. ]
  164. )
  165. set_first_freevar_code = types.CodeType(*args)
  166. def set_closure_cell(cell, value):
  167. # Create a function using the set_first_freevar_code,
  168. # whose first closure cell is `cell`. Calling it will
  169. # change the value of that cell.
  170. setter = types.FunctionType(
  171. set_first_freevar_code, {}, "setter", (), (cell,)
  172. )
  173. # And call it to set the cell.
  174. setter(value)
  175. # Make sure it works on this interpreter:
  176. def make_func_with_cell():
  177. x = None
  178. def func():
  179. return x # pragma: no cover
  180. return func
  181. if PY2:
  182. cell = make_func_with_cell().func_closure[0]
  183. else:
  184. cell = make_func_with_cell().__closure__[0]
  185. set_closure_cell(cell, 100)
  186. if cell.cell_contents != 100:
  187. raise AssertionError # pragma: no cover
  188. except Exception:
  189. return just_warn
  190. else:
  191. return set_closure_cell
  192. set_closure_cell = make_set_closure_cell()