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
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# SPDX-License-Identifier: MIT
4import functools
5import types
7from ._make import _make_ne
10_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="}
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.
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.
29 Args:
30 eq (typing.Callable | None):
31 Callable used to evaluate equality of two objects.
33 lt (typing.Callable | None):
34 Callable used to evaluate whether one object is less than another
35 object.
37 le (typing.Callable | None):
38 Callable used to evaluate whether one object is less than or equal
39 to another object.
41 gt (typing.Callable | None):
42 Callable used to evaluate whether one object is greater than
43 another object.
45 ge (typing.Callable | None):
46 Callable used to evaluate whether one object is greater than or
47 equal to another object.
49 require_same_type (bool):
50 When `True`, equality and ordering methods will return
51 `NotImplemented` if objects are not of the same type.
53 class_name (str | None): Name of class. Defaults to "Comparable".
55 See `comparison` for more details.
57 .. versionadded:: 21.1.0
58 """
60 body = {
61 "__slots__": ["value"],
62 "__init__": _make_init(),
63 "_requirements": [],
64 "_is_comparable_to": _is_comparable_to,
65 }
67 # Add operations.
68 num_order_functions = 0
69 has_eq_function = False
71 if eq is not None:
72 has_eq_function = True
73 body["__eq__"] = _make_operator("eq", eq)
74 body["__ne__"] = _make_ne()
76 if lt is not None:
77 num_order_functions += 1
78 body["__lt__"] = _make_operator("lt", lt)
80 if le is not None:
81 num_order_functions += 1
82 body["__le__"] = _make_operator("le", le)
84 if gt is not None:
85 num_order_functions += 1
86 body["__gt__"] = _make_operator("gt", gt)
88 if ge is not None:
89 num_order_functions += 1
90 body["__ge__"] = _make_operator("ge", ge)
92 type_ = types.new_class(
93 class_name, (object,), {}, lambda ns: ns.update(body)
94 )
96 # Add same type requirement.
97 if require_same_type:
98 type_._requirements.append(_check_same_type)
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_)
109 return type_
112def _make_init():
113 """
114 Create __init__ method.
115 """
117 def __init__(self, value):
118 """
119 Initialize object with *value*.
120 """
121 self.value = value
123 return __init__
126def _make_operator(name, func):
127 """
128 Create operator method.
129 """
131 def method(self, other):
132 if not self._is_comparable_to(other):
133 return NotImplemented
135 result = func(self.value, other.value)
136 if result is NotImplemented:
137 return NotImplemented
139 return result
141 method.__name__ = f"__{name}__"
142 method.__doc__ = (
143 f"Return a {_operation_names[name]} b. Computed by attrs."
144 )
146 return method
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)
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__