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

106 statements  

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 

8 

9__all__ = 'WireRope', 'RopeCore' 

10 

11 

12class RopeCore(object): 

13 """The base rope object. 

14 

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 """ 

18 

19 def __init__(self, callable, rope): 

20 super(RopeCore, self).__init__() 

21 self.callable = callable 

22 self.rope = rope 

23 

24 @property 

25 def wire_class(self): 

26 return self.rope.wire_class 

27 

28 

29class MethodRopeMixin(object): 

30 

31 def __init__(self, *args, **kwargs): 

32 super(MethodRopeMixin, self).__init__(*args, **kwargs) 

33 assert not self.callable.is_barefunction 

34 

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]) 

38 

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 

61 

62 

63class PropertyRopeMixin(object): 

64 

65 def __init__(self, *args, **kwargs): 

66 super(PropertyRopeMixin, self).__init__(*args, **kwargs) 

67 assert not self.callable.is_barefunction 

68 

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]) 

72 

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 

94 

95 

96class FunctionRopeMixin(object): 

97 

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) 

102 

103 def __getattr__(self, name): 

104 try: 

105 return self.__getattribute__(name) 

106 except AttributeError: 

107 pass 

108 return getattr(self._wire, name) 

109 

110 

111class CallableRopeMixin(object): 

112 

113 def __init__(self, *args, **kwargs): 

114 super(CallableRopeMixin, self).__init__(*args, **kwargs) 

115 self.__call__ = functools.wraps(self.callable.wrapped_object)(self) 

116 

117 def __call__(self, *args, **kwargs): 

118 return self._wire(*args, **kwargs) 

119 

120 

121class WireRope(object): 

122 """The end-user wrapper for callables. 

123 

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. 

128 

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 """ 

133 

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 

151 

152 def __call__(self, function): 

153 """Wrap a function/method definition. 

154 

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