callable.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. from __future__ import absolute_import
  2. import types
  3. import six
  4. from ._util import cached_property
  5. from ._compat import inspect
  6. __all__ = ('Callable', )
  7. _inspect_iscoroutinefunction = getattr(
  8. inspect, 'iscoroutinefunction', lambda f: False)
  9. class _Reagent(object):
  10. pass
  11. _reagent = _Reagent()
  12. def _f(owner):
  13. return owner
  14. def _name_binder(descriptor, obj, type):
  15. return type, None
  16. def _type_binder(descriptor, obj, type):
  17. return type, type
  18. def _obj_binder(descriptor, obj, type):
  19. return obj, obj
  20. _descriptor_binder_cache = {}
  21. class Descriptor(object):
  22. def __init__(self, descriptor):
  23. self.descriptor = descriptor
  24. self.descriptor_class = type(descriptor)
  25. def detect_function_attr_name(self):
  26. indicator = object()
  27. descriptor = self.descriptor_class(indicator)
  28. for name in dir(descriptor):
  29. try:
  30. attr = getattr(descriptor, name)
  31. except AttributeError:
  32. continue
  33. if attr is indicator:
  34. # detected!
  35. return name
  36. else:
  37. raise RuntimeError(
  38. "The given function doesn't hold the given function as an "
  39. "attribute. Is it a correct descriptor?")
  40. def detect_property(self, obj, type_):
  41. d = self.descriptor_class(_f)
  42. method_or_value = d.__get__(obj, type_)
  43. return method_or_value is obj or method_or_value is type_
  44. def detect_binder(self, obj, type_):
  45. key = (self.descriptor_class, obj is not None)
  46. if key not in _descriptor_binder_cache:
  47. d = self.descriptor_class(_f)
  48. method = d.__get__(obj, type_)
  49. if isinstance(method, types.FunctionType):
  50. # not a boundmethod - probably staticmethod
  51. binder = _name_binder
  52. elif method is obj:
  53. binder = _obj_binder
  54. elif method is type_:
  55. binder = _type_binder
  56. elif callable(method):
  57. owner = method()
  58. if owner is type_:
  59. binder = _type_binder
  60. elif owner is obj:
  61. binder = _obj_binder
  62. else:
  63. binder = None
  64. elif method is d:
  65. # some non-method descriptor
  66. binder = _name_binder
  67. else:
  68. binder = None
  69. if binder is None:
  70. raise TypeError(
  71. "'descriptor_bind' fails to auto-detect binding rule "
  72. "of the given descriptor. Specify the rule by "
  73. "'wirerope.wire.descriptor_bind.register'.")
  74. _descriptor_binder_cache[key] = binder
  75. else:
  76. binder = _descriptor_binder_cache[key]
  77. return binder
  78. class Callable(object):
  79. """A wrapper object including more information of callables."""
  80. def __init__(self, f):
  81. self.wrapped_object = f
  82. self.is_function_type = type(self) is types.FunctionType # noqa
  83. if self.is_descriptor:
  84. self.descriptor = Descriptor(f)
  85. f = getattr(f, self.descriptor.detect_function_attr_name())
  86. else:
  87. self.descriptor = None
  88. self.wrapped_callable = f
  89. self.is_wrapped_coroutine = getattr(f, '_is_coroutine', None)
  90. self.is_coroutine = self.is_wrapped_coroutine or \
  91. _inspect_iscoroutinefunction(f)
  92. @cached_property
  93. def signature(self):
  94. return inspect.signature(self.wrapped_callable)
  95. @cached_property
  96. def parameters(self):
  97. return list(self.signature.parameters.values())
  98. @property
  99. def first_parameter(self):
  100. return self.parameters[0] if self.parameters else None
  101. @cached_property
  102. def is_boundmethod(self):
  103. if self.is_function_type or self.is_builtin_property:
  104. return False
  105. new_bound = self.wrapped_object.__get__(object())
  106. try:
  107. if six.PY2:
  108. return new_bound is self.wrapped_object
  109. else:
  110. return type(new_bound) is type(self.wrapped_object) # noqa
  111. except Exception:
  112. return False
  113. if six.PY2:
  114. @property
  115. def is_unboundmethod(self):
  116. return type(self.wrapped_object) is type(Callable.__init__) # noqa
  117. @cached_property
  118. def is_descriptor(self):
  119. if self.is_boundmethod:
  120. return False
  121. is_descriptor = type(self.wrapped_object).__get__ \
  122. is not types.FunctionType.__get__ # noqa
  123. if six.PY2:
  124. is_descriptor = is_descriptor and \
  125. not (self.is_unboundmethod or self.is_boundmethod)
  126. return is_descriptor
  127. @cached_property
  128. def is_builtin_property(self):
  129. return issubclass(type(self.wrapped_object), property)
  130. @cached_property
  131. def is_property(self):
  132. return self.is_builtin_property or \
  133. (self.is_descriptor and self.descriptor.detect_property(
  134. _reagent, _Reagent))
  135. if six.PY34:
  136. @cached_property
  137. def is_barefunction(self):
  138. cc = self.wrapped_callable
  139. method_name = cc.__qualname__.split('<locals>.')[-1]
  140. if method_name == cc.__name__:
  141. return True
  142. return False
  143. else:
  144. @cached_property
  145. def is_barefunction(self):
  146. # im_class does not exist at this point
  147. if self.is_descriptor:
  148. return False
  149. if six.PY2:
  150. if self.is_unboundmethod:
  151. return False
  152. return not (self.is_membermethod or self.is_classmethod)
  153. @cached_property
  154. def is_member(self):
  155. """Test given argument is a method or not.
  156. :rtype: bool
  157. :note: The test is partially based on the first parameter name.
  158. The test result might be wrong.
  159. """
  160. if six.PY34:
  161. if self.is_barefunction:
  162. return False
  163. if not self.is_descriptor:
  164. return True
  165. return self.first_parameter is not None \
  166. and self.first_parameter.name == 'self'
  167. @cached_property
  168. def is_membermethod(self):
  169. """Test given argument is a method or not.
  170. :rtype: bool
  171. :note: The test is partially based on the first parameter name.
  172. The test result might be wrong.
  173. """
  174. if self.is_boundmethod:
  175. return False
  176. if self.is_property:
  177. return False
  178. return self.is_member
  179. @cached_property
  180. def is_classmethod(self):
  181. """Test given argument is a classmethod or not.
  182. :rtype: bool
  183. :note: The test is partially based on the first parameter name.
  184. The test result might be wrong.
  185. """
  186. if isinstance(self.wrapped_object, classmethod):
  187. return True
  188. if six.PY34:
  189. if self.is_barefunction:
  190. return False
  191. if not self.is_descriptor:
  192. return False
  193. return self.first_parameter is not None \
  194. and self.first_parameter.name == 'cls'