Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/attr/_cmp.py: 17%

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

52 statements  

1# SPDX-License-Identifier: MIT 

2 

3 

4import functools 

5import types 

6 

7from ._make import _make_ne 

8 

9 

10_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} 

11 

12 

13def cmp_using( 

14 eq=None, 

15 lt=None, 

16 le=None, 

17 gt=None, 

18 ge=None, 

19 require_same_type=True, 

20 class_name="Comparable", 

21): 

22 """ 

23 Create a class that can be passed into `attrs.field`'s ``eq``, ``order``, 

24 and ``cmp`` arguments to customize field comparison. 

25 

26 The resulting class will have a full set of ordering methods if at least 

27 one of ``{lt, le, gt, ge}`` and ``eq`` are provided. 

28 

29 Args: 

30 eq (typing.Callable | None): 

31 Callable used to evaluate equality of two objects. 

32 

33 lt (typing.Callable | None): 

34 Callable used to evaluate whether one object is less than another 

35 object. 

36 

37 le (typing.Callable | None): 

38 Callable used to evaluate whether one object is less than or equal 

39 to another object. 

40 

41 gt (typing.Callable | None): 

42 Callable used to evaluate whether one object is greater than 

43 another object. 

44 

45 ge (typing.Callable | None): 

46 Callable used to evaluate whether one object is greater than or 

47 equal to another object. 

48 

49 require_same_type (bool): 

50 When `True`, equality and ordering methods will return 

51 `NotImplemented` if objects are not of the same type. 

52 

53 class_name (str | None): Name of class. Defaults to "Comparable". 

54 

55 See `comparison` for more details. 

56 

57 .. versionadded:: 21.1.0 

58 """ 

59 

60 body = { 

61 "__slots__": ["value"], 

62 "__init__": _make_init(), 

63 "_requirements": [], 

64 "_is_comparable_to": _is_comparable_to, 

65 } 

66 

67 # Add operations. 

68 num_order_functions = 0 

69 has_eq_function = False 

70 

71 if eq is not None: 

72 has_eq_function = True 

73 body["__eq__"] = _make_operator("eq", eq) 

74 body["__ne__"] = _make_ne() 

75 

76 if lt is not None: 

77 num_order_functions += 1 

78 body["__lt__"] = _make_operator("lt", lt) 

79 

80 if le is not None: 

81 num_order_functions += 1 

82 body["__le__"] = _make_operator("le", le) 

83 

84 if gt is not None: 

85 num_order_functions += 1 

86 body["__gt__"] = _make_operator("gt", gt) 

87 

88 if ge is not None: 

89 num_order_functions += 1 

90 body["__ge__"] = _make_operator("ge", ge) 

91 

92 type_ = types.new_class( 

93 class_name, (object,), {}, lambda ns: ns.update(body) 

94 ) 

95 

96 # Add same type requirement. 

97 if require_same_type: 

98 type_._requirements.append(_check_same_type) 

99 

100 # Add total ordering if at least one operation was defined. 

101 if 0 < num_order_functions < 4: 

102 if not has_eq_function: 

103 # functools.total_ordering requires __eq__ to be defined, 

104 # so raise early error here to keep a nice stack. 

105 msg = "eq must be define is order to complete ordering from lt, le, gt, ge." 

106 raise ValueError(msg) 

107 type_ = functools.total_ordering(type_) 

108 

109 return type_ 

110 

111 

112def _make_init(): 

113 """ 

114 Create __init__ method. 

115 """ 

116 

117 def __init__(self, value): 

118 """ 

119 Initialize object with *value*. 

120 """ 

121 self.value = value 

122 

123 return __init__ 

124 

125 

126def _make_operator(name, func): 

127 """ 

128 Create operator method. 

129 """ 

130 

131 def method(self, other): 

132 if not self._is_comparable_to(other): 

133 return NotImplemented 

134 

135 result = func(self.value, other.value) 

136 if result is NotImplemented: 

137 return NotImplemented 

138 

139 return result 

140 

141 method.__name__ = f"__{name}__" 

142 method.__doc__ = ( 

143 f"Return a {_operation_names[name]} b. Computed by attrs." 

144 ) 

145 

146 return method 

147 

148 

149def _is_comparable_to(self, other): 

150 """ 

151 Check whether `other` is comparable to `self`. 

152 """ 

153 return all(func(self, other) for func in self._requirements) 

154 

155 

156def _check_same_type(self, other): 

157 """ 

158 Return True if *self* and *other* are of the same type, False otherwise. 

159 """ 

160 return other.value.__class__ is self.value.__class__