123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- from __future__ import absolute_import
- import types
- import six
- from ._util import cached_property
- from ._compat import inspect
- __all__ = ('Callable', )
- _inspect_iscoroutinefunction = getattr(
- inspect, 'iscoroutinefunction', lambda f: False)
- class _Reagent(object):
- pass
- _reagent = _Reagent()
- def _f(owner):
- return owner
- def _name_binder(descriptor, obj, type):
- return type, None
- def _type_binder(descriptor, obj, type):
- return type, type
- def _obj_binder(descriptor, obj, type):
- return obj, obj
- _descriptor_binder_cache = {}
- class Descriptor(object):
- def __init__(self, descriptor):
- self.descriptor = descriptor
- self.descriptor_class = type(descriptor)
- def detect_function_attr_name(self):
- indicator = object()
- descriptor = self.descriptor_class(indicator)
- for name in dir(descriptor):
- try:
- attr = getattr(descriptor, name)
- except AttributeError:
- continue
- if attr is indicator:
- # detected!
- return name
- else:
- raise RuntimeError(
- "The given function doesn't hold the given function as an "
- "attribute. Is it a correct descriptor?")
- def detect_property(self, obj, type_):
- d = self.descriptor_class(_f)
- method_or_value = d.__get__(obj, type_)
- return method_or_value is obj or method_or_value is type_
- def detect_binder(self, obj, type_):
- key = (self.descriptor_class, obj is not None)
- if key not in _descriptor_binder_cache:
- d = self.descriptor_class(_f)
- method = d.__get__(obj, type_)
- if isinstance(method, types.FunctionType):
- # not a boundmethod - probably staticmethod
- binder = _name_binder
- elif method is obj:
- binder = _obj_binder
- elif method is type_:
- binder = _type_binder
- elif callable(method):
- owner = method()
- if owner is type_:
- binder = _type_binder
- elif owner is obj:
- binder = _obj_binder
- else:
- binder = None
- elif method is d:
- # some non-method descriptor
- binder = _name_binder
- else:
- binder = None
- if binder is None:
- raise TypeError(
- "'descriptor_bind' fails to auto-detect binding rule "
- "of the given descriptor. Specify the rule by "
- "'wirerope.wire.descriptor_bind.register'.")
- _descriptor_binder_cache[key] = binder
- else:
- binder = _descriptor_binder_cache[key]
- return binder
- class Callable(object):
- """A wrapper object including more information of callables."""
- def __init__(self, f):
- self.wrapped_object = f
- self.is_function_type = type(self) is types.FunctionType # noqa
- if self.is_descriptor:
- self.descriptor = Descriptor(f)
- f = getattr(f, self.descriptor.detect_function_attr_name())
- else:
- self.descriptor = None
- self.wrapped_callable = f
- self.is_wrapped_coroutine = getattr(f, '_is_coroutine', None)
- self.is_coroutine = self.is_wrapped_coroutine or \
- _inspect_iscoroutinefunction(f)
- @cached_property
- def signature(self):
- return inspect.signature(self.wrapped_callable)
- @cached_property
- def parameters(self):
- return list(self.signature.parameters.values())
- @property
- def first_parameter(self):
- return self.parameters[0] if self.parameters else None
- @cached_property
- def is_boundmethod(self):
- if self.is_function_type or self.is_builtin_property:
- return False
- new_bound = self.wrapped_object.__get__(object())
- try:
- if six.PY2:
- return new_bound is self.wrapped_object
- else:
- return type(new_bound) is type(self.wrapped_object) # noqa
- except Exception:
- return False
- if six.PY2:
- @property
- def is_unboundmethod(self):
- return type(self.wrapped_object) is type(Callable.__init__) # noqa
- @cached_property
- def is_descriptor(self):
- if self.is_boundmethod:
- return False
- is_descriptor = type(self.wrapped_object).__get__ \
- is not types.FunctionType.__get__ # noqa
- if six.PY2:
- is_descriptor = is_descriptor and \
- not (self.is_unboundmethod or self.is_boundmethod)
- return is_descriptor
- @cached_property
- def is_builtin_property(self):
- return issubclass(type(self.wrapped_object), property)
- @cached_property
- def is_property(self):
- return self.is_builtin_property or \
- (self.is_descriptor and self.descriptor.detect_property(
- _reagent, _Reagent))
- if six.PY34:
- @cached_property
- def is_barefunction(self):
- cc = self.wrapped_callable
- method_name = cc.__qualname__.split('<locals>.')[-1]
- if method_name == cc.__name__:
- return True
- return False
- else:
- @cached_property
- def is_barefunction(self):
- # im_class does not exist at this point
- if self.is_descriptor:
- return False
- if six.PY2:
- if self.is_unboundmethod:
- return False
- return not (self.is_membermethod or self.is_classmethod)
- @cached_property
- def is_member(self):
- """Test given argument is a method or not.
- :rtype: bool
- :note: The test is partially based on the first parameter name.
- The test result might be wrong.
- """
- if six.PY34:
- if self.is_barefunction:
- return False
- if not self.is_descriptor:
- return True
- return self.first_parameter is not None \
- and self.first_parameter.name == 'self'
- @cached_property
- def is_membermethod(self):
- """Test given argument is a method or not.
- :rtype: bool
- :note: The test is partially based on the first parameter name.
- The test result might be wrong.
- """
- if self.is_boundmethod:
- return False
- if self.is_property:
- return False
- return self.is_member
- @cached_property
- def is_classmethod(self):
- """Test given argument is a classmethod or not.
- :rtype: bool
- :note: The test is partially based on the first parameter name.
- The test result might be wrong.
- """
- if isinstance(self.wrapped_object, classmethod):
- return True
- if six.PY34:
- if self.is_barefunction:
- return False
- if not self.is_descriptor:
- return False
- return self.first_parameter is not None \
- and self.first_parameter.name == 'cls'
|