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 :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 msg = "eq must be define is order to complete ordering from lt, le, gt, ge." 

96 raise ValueError(msg) 

97 type_ = functools.total_ordering(type_) 

98 

99 return type_ 

100 

101 

102def _make_init(): 

103 """ 

104 Create __init__ method. 

105 """ 

106 

107 def __init__(self, value): 

108 """ 

109 Initialize object with *value*. 

110 """ 

111 self.value = value 

112 

113 return __init__ 

114 

115 

116def _make_operator(name, func): 

117 """ 

118 Create operator method. 

119 """ 

120 

121 def method(self, other): 

122 if not self._is_comparable_to(other): 

123 return NotImplemented 

124 

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

126 if result is NotImplemented: 

127 return NotImplemented 

128 

129 return result 

130 

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

132 method.__doc__ = ( 

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

134 ) 

135 

136 return method 

137 

138 

139def _is_comparable_to(self, other): 

140 """ 

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

142 """ 

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

144 

145 

146def _check_same_type(self, other): 

147 """ 

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

149 """ 

150 return other.value.__class__ is self.value.__class__