Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/wirerope/rope.py: 58%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""":mod:`wirerope.rope` --- Wire access dispatcher for descriptor type.
2=======================================================================
3"""
4import six
5from .callable import Callable
6from .wire import descriptor_bind
7from ._compat import functools
9__all__ = 'WireRope', 'RopeCore'
12class RopeCore(object):
13 """The base rope object.
15 To change rope behavior, create a subclass or compatible class
16 and pass it to `core_class` argument of :class wirerope.rope.WireRope`.
17 """
19 def __init__(self, callable, rope):
20 super(RopeCore, self).__init__()
21 self.callable = callable
22 self.rope = rope
24 @property
25 def wire_class(self):
26 return self.rope.wire_class
29class MethodRopeMixin(object):
31 def __init__(self, *args, **kwargs):
32 super(MethodRopeMixin, self).__init__(*args, **kwargs)
33 assert not self.callable.is_barefunction
35 def __set_name__(self, owner, name):
36 # Use a non-identifier character as separator to prevent name clash.
37 self.wire_name = '|'.join(['__wire', owner.__name__, name])
39 def __get__(self, obj, type=None):
40 cw = self.callable
41 co = cw.wrapped_object
42 owner, _ = descriptor_bind(co, obj, type)
43 if owner is None: # invalid binding but still wire it
44 owner = obj if obj is not None else type
45 if hasattr(self, 'wire_name'):
46 wire_name = self.wire_name
47 # Lookup in `__dict__` instead of using `getattr`, because
48 # `getattr` falls back to class attributes.
49 wire = owner.__dict__.get(wire_name)
50 else:
51 wire_name_parts = ['__wire_', cw.wrapped_callable.__name__]
52 if owner is type:
53 wire_name_parts.extend(('_', type.__name__))
54 wire_name = ''.join(wire_name_parts)
55 wire = getattr(owner, wire_name, None)
56 if wire is None:
57 wire = self.wire_class(self, owner, (obj, type))
58 setattr(owner, wire_name, wire)
59 assert callable(wire.__func__)
60 return wire
63class PropertyRopeMixin(object):
65 def __init__(self, *args, **kwargs):
66 super(PropertyRopeMixin, self).__init__(*args, **kwargs)
67 assert not self.callable.is_barefunction
69 def __set_name__(self, owner, name):
70 # Use a non-identifier character as separator to prevent name clash.
71 self.wire_name = '|'.join(['__wire', owner.__name__, name])
73 def __get__(self, obj, type=None):
74 cw = self.callable
75 co = cw.wrapped_object
76 owner, _ = descriptor_bind(co, obj, type)
77 if owner is None: # invalid binding but still wire it
78 owner = obj if obj is not None else type
79 if hasattr(self, 'wire_name'):
80 wire_name = self.wire_name
81 # Lookup in `__dict__` instead of using `getattr`, because
82 # `getattr` falls back to class attributes.
83 wire = owner.__dict__.get(wire_name)
84 else:
85 wire_name_parts = ['__wire_', cw.wrapped_callable.__name__]
86 if owner is type:
87 wire_name_parts.extend(('_', type.__name__))
88 wire_name = ''.join(wire_name_parts)
89 wire = getattr(owner, wire_name, None)
90 if wire is None:
91 wire = self.wire_class(self, owner, (obj, type))
92 setattr(owner, wire_name, wire)
93 return wire._on_property() # requires property path
96class FunctionRopeMixin(object):
98 def __init__(self, *args, **kwargs):
99 super(FunctionRopeMixin, self).__init__(*args, **kwargs)
100 assert self.callable.is_barefunction or self.callable.is_boundmethod
101 self._wire = self.wire_class(self, None, None)
103 def __getattr__(self, name):
104 try:
105 return self.__getattribute__(name)
106 except AttributeError:
107 pass
108 return getattr(self._wire, name)
111class CallableRopeMixin(object):
113 def __init__(self, *args, **kwargs):
114 super(CallableRopeMixin, self).__init__(*args, **kwargs)
115 self.__call__ = functools.wraps(self.callable.wrapped_object)(self)
117 def __call__(self, *args, **kwargs):
118 return self._wire(*args, **kwargs)
121class WireRope(object):
122 """The end-user wrapper for callables.
124 Any callable can be wrapped by this class regardless of its concept -
125 free function, method, property or even more weird one.
126 The calling type is decided by each call and redirected to proper
127 RopeMixin.
129 The rope will detect method or property owner by needs. It also will return
130 or call their associated `wirerope.wire.Wire` object - which are the user
131 defined behavior.
132 """
134 def __init__(
135 self, wire_class, core_class=RopeCore,
136 wraps=False, rope_args=None):
137 self.wire_class = wire_class
138 self.method_rope = type(
139 '_MethodRope', (MethodRopeMixin, core_class), {})
140 self.property_rope = type(
141 '_PropertyRope', (PropertyRopeMixin, core_class), {})
142 self.function_rope = type(
143 '_FunctionRope', (FunctionRopeMixin, core_class), {})
144 self.callable_function_rope = type(
145 '_CallableFunctionRope',
146 (CallableRopeMixin, FunctionRopeMixin, core_class), {})
147 for rope in (self, self.method_rope, self.property_rope,
148 self.function_rope, self.callable_function_rope):
149 rope._wrapped = wraps
150 rope._args = rope_args
152 def __call__(self, function):
153 """Wrap a function/method definition.
155 :return: Rope object. The return type is up to given callable is
156 function, method or property.
157 """
158 cw = Callable(function)
159 if cw.is_barefunction or cw.is_boundmethod:
160 rope_class = self.callable_function_rope
161 wire_class_call = self.wire_class.__call__
162 if six.PY3:
163 if wire_class_call.__qualname__ == 'type.__call__':
164 rope_class = self.function_rope
165 else:
166 # method-wrapper test for CPython2.7
167 # im_class == type test for PyPy2.7
168 if type(wire_class_call).__name__ == 'method-wrapper' or \
169 wire_class_call.im_class == type:
170 rope_class = self.function_rope
171 elif cw.is_property:
172 rope_class = self.property_rope
173 else:
174 rope_class = self.method_rope
175 rope = rope_class(cw, rope=self)
176 if rope._wrapped:
177 rope = functools.wraps(cw.wrapped_callable)(rope)
178 return rope