Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

_compat.py 5.7KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. # SPDX-License-Identifier: MIT
  2. import inspect
  3. import platform
  4. import sys
  5. import threading
  6. import types
  7. import warnings
  8. from collections.abc import Mapping, Sequence # noqa
  9. from typing import _GenericAlias
  10. PYPY = platform.python_implementation() == "PyPy"
  11. PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
  12. PY310 = sys.version_info[:2] >= (3, 10)
  13. PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
  14. def just_warn(*args, **kw):
  15. warnings.warn(
  16. "Running interpreter doesn't sufficiently support code object "
  17. "introspection. Some features like bare super() or accessing "
  18. "__class__ will not work with slotted classes.",
  19. RuntimeWarning,
  20. stacklevel=2,
  21. )
  22. class _AnnotationExtractor:
  23. """
  24. Extract type annotations from a callable, returning None whenever there
  25. is none.
  26. """
  27. __slots__ = ["sig"]
  28. def __init__(self, callable):
  29. try:
  30. self.sig = inspect.signature(callable)
  31. except (ValueError, TypeError): # inspect failed
  32. self.sig = None
  33. def get_first_param_type(self):
  34. """
  35. Return the type annotation of the first argument if it's not empty.
  36. """
  37. if not self.sig:
  38. return None
  39. params = list(self.sig.parameters.values())
  40. if params and params[0].annotation is not inspect.Parameter.empty:
  41. return params[0].annotation
  42. return None
  43. def get_return_type(self):
  44. """
  45. Return the return type if it's not empty.
  46. """
  47. if (
  48. self.sig
  49. and self.sig.return_annotation is not inspect.Signature.empty
  50. ):
  51. return self.sig.return_annotation
  52. return None
  53. def make_set_closure_cell():
  54. """Return a function of two arguments (cell, value) which sets
  55. the value stored in the closure cell `cell` to `value`.
  56. """
  57. # pypy makes this easy. (It also supports the logic below, but
  58. # why not do the easy/fast thing?)
  59. if PYPY:
  60. def set_closure_cell(cell, value):
  61. cell.__setstate__((value,))
  62. return set_closure_cell
  63. # Otherwise gotta do it the hard way.
  64. try:
  65. if sys.version_info >= (3, 8):
  66. def set_closure_cell(cell, value):
  67. cell.cell_contents = value
  68. else:
  69. # Create a function that will set its first cellvar to `value`.
  70. def set_first_cellvar_to(value):
  71. x = value
  72. return
  73. # This function will be eliminated as dead code, but
  74. # not before its reference to `x` forces `x` to be
  75. # represented as a closure cell rather than a local.
  76. def force_x_to_be_a_cell(): # pragma: no cover
  77. return x
  78. # Extract the code object and make sure our assumptions about
  79. # the closure behavior are correct.
  80. co = set_first_cellvar_to.__code__
  81. if co.co_cellvars != ("x",) or co.co_freevars != ():
  82. raise AssertionError # pragma: no cover
  83. # Convert this code object to a code object that sets the
  84. # function's first _freevar_ (not cellvar) to the argument.
  85. args = [co.co_argcount]
  86. args.append(co.co_kwonlyargcount)
  87. args.extend(
  88. [
  89. co.co_nlocals,
  90. co.co_stacksize,
  91. co.co_flags,
  92. co.co_code,
  93. co.co_consts,
  94. co.co_names,
  95. co.co_varnames,
  96. co.co_filename,
  97. co.co_name,
  98. co.co_firstlineno,
  99. co.co_lnotab,
  100. # These two arguments are reversed:
  101. co.co_cellvars,
  102. co.co_freevars,
  103. ]
  104. )
  105. set_first_freevar_code = types.CodeType(*args)
  106. def set_closure_cell(cell, value):
  107. # Create a function using the set_first_freevar_code,
  108. # whose first closure cell is `cell`. Calling it will
  109. # change the value of that cell.
  110. setter = types.FunctionType(
  111. set_first_freevar_code, {}, "setter", (), (cell,)
  112. )
  113. # And call it to set the cell.
  114. setter(value)
  115. # Make sure it works on this interpreter:
  116. def make_func_with_cell():
  117. x = None
  118. def func():
  119. return x # pragma: no cover
  120. return func
  121. cell = make_func_with_cell().__closure__[0]
  122. set_closure_cell(cell, 100)
  123. if cell.cell_contents != 100:
  124. raise AssertionError # pragma: no cover
  125. except Exception:
  126. return just_warn
  127. else:
  128. return set_closure_cell
  129. set_closure_cell = make_set_closure_cell()
  130. # Thread-local global to track attrs instances which are already being repr'd.
  131. # This is needed because there is no other (thread-safe) way to pass info
  132. # about the instances that are already being repr'd through the call stack
  133. # in order to ensure we don't perform infinite recursion.
  134. #
  135. # For instance, if an instance contains a dict which contains that instance,
  136. # we need to know that we're already repr'ing the outside instance from within
  137. # the dict's repr() call.
  138. #
  139. # This lives here rather than in _make.py so that the functions in _make.py
  140. # don't have a direct reference to the thread-local in their globals dict.
  141. # If they have such a reference, it breaks cloudpickle.
  142. repr_context = threading.local()
  143. def get_generic_base(cl):
  144. """If this is a generic class (A[str]), return the generic base for it."""
  145. if cl.__class__ is _GenericAlias:
  146. return cl.__origin__
  147. return None