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

54 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:51 +0000

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 :param Optional[callable] eq: `callable` used to evaluate equality of two 

30 objects. 

31 :param Optional[callable] lt: `callable` used to evaluate whether one 

32 object is less than another object. 

33 :param Optional[callable] le: `callable` used to evaluate whether one 

34 object is less than or equal to another object. 

35 :param Optional[callable] gt: `callable` used to evaluate whether one 

36 object is greater than another object. 

37 :param Optional[callable] ge: `callable` used to evaluate whether one 

38 object is greater than or equal to another object. 

39 

40 :param bool require_same_type: When `True`, equality and ordering methods 

41 will return `NotImplemented` if objects are not of the same type. 

42 

43 :param Optional[str] class_name: Name of class. Defaults to 'Comparable'. 

44 

45 See `comparison` for more details. 

46 

47 .. versionadded:: 21.1.0 

48 """ 

49 

50 body = { 

51 "__slots__": ["value"], 

52 "__init__": _make_init(), 

53 "_requirements": [], 

54 "_is_comparable_to": _is_comparable_to, 

55 } 

56 

57 # Add operations. 

58 num_order_functions = 0 

59 has_eq_function = False 

60 

61 if eq is not None: 

62 has_eq_function = True 

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

64 body["__ne__"] = _make_ne() 

65 

66 if lt is not None: 

67 num_order_functions += 1 

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

69 

70 if le is not None: 

71 num_order_functions += 1 

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

73 

74 if gt is not None: 

75 num_order_functions += 1 

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

77 

78 if ge is not None: 

79 num_order_functions += 1 

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

81 

82 type_ = types.new_class( 

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

84 ) 

85 

86 # Add same type requirement. 

87 if require_same_type: 

88 type_._requirements.append(_check_same_type) 

89 

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

91 if 0 < num_order_functions < 4: 

92 if not has_eq_function: 

93 # functools.total_ordering requires __eq__ to be defined, 

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

95 raise ValueError( 

96 "eq must be define is order to complete ordering from " 

97 "lt, le, gt, ge." 

98 ) 

99 type_ = functools.total_ordering(type_) 

100 

101 return type_ 

102 

103 

104def _make_init(): 

105 """ 

106 Create __init__ method. 

107 """ 

108 

109 def __init__(self, value): 

110 """ 

111 Initialize object with *value*. 

112 """ 

113 self.value = value 

114 

115 return __init__ 

116 

117 

118def _make_operator(name, func): 

119 """ 

120 Create operator method. 

121 """ 

122 

123 def method(self, other): 

124 if not self._is_comparable_to(other): 

125 return NotImplemented 

126 

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

128 if result is NotImplemented: 

129 return NotImplemented 

130 

131 return result 

132 

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

134 method.__doc__ = ( 

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

136 ) 

137 

138 return method 

139 

140 

141def _is_comparable_to(self, other): 

142 """ 

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

144 """ 

145 for func in self._requirements: 

146 if not func(self, other): 

147 return False 

148 return True 

149 

150 

151def _check_same_type(self, other): 

152 """ 

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

154 """ 

155 return other.value.__class__ is self.value.__class__