comparison mercurial/thirdparty/attr/_cmp.py @ 49643:e1c586b9a43c

attr: vendor 22.1.0 The previous version was 5 years old, and pytype 2022.06.30 started complaining about various uses (e.g. seeing `mercurial.thirdparty.attr._make._CountingAttr` instead of `bytearray`). Hopefully this helps. Additionally, this has official python 3.11 support. The `attrs` package is left out, because it is simply a bunch of *.pyi stubs and `from attr.X import *`, and that's not how they've been used up to this point. We'd probably need to customize those anyway to `from mercurial.thirdparty.attr import *`.
author Matt Harbison <matt_harbison@yahoo.com>
date Mon, 21 Nov 2022 15:04:42 -0500
parents
children
comparison
equal deleted inserted replaced
49642:7e6f3c69c0fb 49643:e1c586b9a43c
1 # SPDX-License-Identifier: MIT
2
3
4 import functools
5 import types
6
7 from ._make import _make_ne
8
9
10 _operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="}
11
12
13 def 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 `attr.ib`'s ``eq``, ``order``, and
24 ``cmp`` arguments to customize field comparison.
25
26 The resulting class will have a full set of ordering methods if
27 at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
28
29 :param Optional[callable] eq: `callable` used to evaluate equality
30 of two objects.
31 :param Optional[callable] lt: `callable` used to evaluate whether
32 one object is less than another object.
33 :param Optional[callable] le: `callable` used to evaluate whether
34 one object is less than or equal to another object.
35 :param Optional[callable] gt: `callable` used to evaluate whether
36 one object is greater than another object.
37 :param Optional[callable] ge: `callable` used to evaluate whether
38 one 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
104 def _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
118 def _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__ = "__%s__" % (name,)
134 method.__doc__ = "Return a %s b. Computed by attrs." % (
135 _operation_names[name],
136 )
137
138 return method
139
140
141 def _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
151 def _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__