You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.4 KiB

4 years ago
  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 UserDict import IterableUserDict
  15. # We 'bundle' isclass instead of using inspect as importing inspect is
  16. # fairly expensive (order of 10-15 ms for a modern machine in 2016)
  17. def isclass(klass):
  18. return isinstance(klass, (type, types.ClassType))
  19. # TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
  20. TYPE = "type"
  21. def iteritems(d):
  22. return d.iteritems()
  23. # Python 2 is bereft of a read-only dict proxy, so we make one!
  24. class ReadOnlyDict(IterableUserDict):
  25. """
  26. Best-effort read-only dict wrapper.
  27. """
  28. def __setitem__(self, key, val):
  29. # We gently pretend we're a Python 3 mappingproxy.
  30. raise TypeError(
  31. "'mappingproxy' object does not support item assignment"
  32. )
  33. def update(self, _):
  34. # We gently pretend we're a Python 3 mappingproxy.
  35. raise AttributeError(
  36. "'mappingproxy' object has no attribute 'update'"
  37. )
  38. def __delitem__(self, _):
  39. # We gently pretend we're a Python 3 mappingproxy.
  40. raise TypeError(
  41. "'mappingproxy' object does not support item deletion"
  42. )
  43. def clear(self):
  44. # We gently pretend we're a Python 3 mappingproxy.
  45. raise AttributeError(
  46. "'mappingproxy' object has no attribute 'clear'"
  47. )
  48. def pop(self, key, default=None):
  49. # We gently pretend we're a Python 3 mappingproxy.
  50. raise AttributeError(
  51. "'mappingproxy' object has no attribute 'pop'"
  52. )
  53. def popitem(self):
  54. # We gently pretend we're a Python 3 mappingproxy.
  55. raise AttributeError(
  56. "'mappingproxy' object has no attribute 'popitem'"
  57. )
  58. def setdefault(self, key, default=None):
  59. # We gently pretend we're a Python 3 mappingproxy.
  60. raise AttributeError(
  61. "'mappingproxy' object has no attribute 'setdefault'"
  62. )
  63. def __repr__(self):
  64. # Override to be identical to the Python 3 version.
  65. return "mappingproxy(" + repr(self.data) + ")"
  66. def metadata_proxy(d):
  67. res = ReadOnlyDict()
  68. res.data.update(d) # We blocked update, so we have to do it like this.
  69. return res
  70. else:
  71. def isclass(klass):
  72. return isinstance(klass, type)
  73. TYPE = "class"
  74. def iteritems(d):
  75. return d.items()
  76. def metadata_proxy(d):
  77. return types.MappingProxyType(dict(d))
  78. def import_ctypes():
  79. """
  80. Moved into a function for testability.
  81. """
  82. import ctypes
  83. return ctypes
  84. if not PY2:
  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. "Missing ctypes. Some features like bare super() or accessing "
  92. "__class__ will not work with slots classes.",
  93. RuntimeWarning,
  94. stacklevel=2,
  95. )
  96. else:
  97. def just_warn(*args, **kw): # pragma: nocover
  98. """
  99. We only warn on Python 3 because we are not aware of any concrete
  100. consequences of not setting the cell on Python 2.
  101. """
  102. def make_set_closure_cell():
  103. """
  104. Moved into a function for testability.
  105. """
  106. if PYPY: # pragma: no cover
  107. def set_closure_cell(cell, value):
  108. cell.__setstate__((value,))
  109. else:
  110. try:
  111. ctypes = import_ctypes()
  112. set_closure_cell = ctypes.pythonapi.PyCell_Set
  113. set_closure_cell.argtypes = (ctypes.py_object, ctypes.py_object)
  114. set_closure_cell.restype = ctypes.c_int
  115. except Exception:
  116. # We try best effort to set the cell, but sometimes it's not
  117. # possible. For example on Jython or on GAE.
  118. set_closure_cell = just_warn
  119. return set_closure_cell
  120. set_closure_cell = make_set_closure_cell()