Skip to content

Commit 1f7d646

Browse files
authored
bpo-44605: Teach @total_ordering() to work with metaclasses (GH-27633)
1 parent 7d747f2 commit 1f7d646

File tree

3 files changed

+41
-12
lines changed

3 files changed

+41
-12
lines changed

Lib/functools.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,84 +88,84 @@ def wraps(wrapped,
8888

8989
def _gt_from_lt(self, other, NotImplemented=NotImplemented):
9090
'Return a > b. Computed by @total_ordering from (not a < b) and (a != b).'
91-
op_result = self.__lt__(other)
91+
op_result = type(self).__lt__(self, other)
9292
if op_result is NotImplemented:
9393
return op_result
9494
return not op_result and self != other
9595

9696
def _le_from_lt(self, other, NotImplemented=NotImplemented):
9797
'Return a <= b. Computed by @total_ordering from (a < b) or (a == b).'
98-
op_result = self.__lt__(other)
98+
op_result = type(self).__lt__(self, other)
9999
if op_result is NotImplemented:
100100
return op_result
101101
return op_result or self == other
102102

103103
def _ge_from_lt(self, other, NotImplemented=NotImplemented):
104104
'Return a >= b. Computed by @total_ordering from (not a < b).'
105-
op_result = self.__lt__(other)
105+
op_result = type(self).__lt__(self, other)
106106
if op_result is NotImplemented:
107107
return op_result
108108
return not op_result
109109

110110
def _ge_from_le(self, other, NotImplemented=NotImplemented):
111111
'Return a >= b. Computed by @total_ordering from (not a <= b) or (a == b).'
112-
op_result = self.__le__(other)
112+
op_result = type(self).__le__(self, other)
113113
if op_result is NotImplemented:
114114
return op_result
115115
return not op_result or self == other
116116

117117
def _lt_from_le(self, other, NotImplemented=NotImplemented):
118118
'Return a < b. Computed by @total_ordering from (a <= b) and (a != b).'
119-
op_result = self.__le__(other)
119+
op_result = type(self).__le__(self, other)
120120
if op_result is NotImplemented:
121121
return op_result
122122
return op_result and self != other
123123

124124
def _gt_from_le(self, other, NotImplemented=NotImplemented):
125125
'Return a > b. Computed by @total_ordering from (not a <= b).'
126-
op_result = self.__le__(other)
126+
op_result = type(self).__le__(self, other)
127127
if op_result is NotImplemented:
128128
return op_result
129129
return not op_result
130130

131131
def _lt_from_gt(self, other, NotImplemented=NotImplemented):
132132
'Return a < b. Computed by @total_ordering from (not a > b) and (a != b).'
133-
op_result = self.__gt__(other)
133+
op_result = type(self).__gt__(self, other)
134134
if op_result is NotImplemented:
135135
return op_result
136136
return not op_result and self != other
137137

138138
def _ge_from_gt(self, other, NotImplemented=NotImplemented):
139139
'Return a >= b. Computed by @total_ordering from (a > b) or (a == b).'
140-
op_result = self.__gt__(other)
140+
op_result = type(self).__gt__(self, other)
141141
if op_result is NotImplemented:
142142
return op_result
143143
return op_result or self == other
144144

145145
def _le_from_gt(self, other, NotImplemented=NotImplemented):
146146
'Return a <= b. Computed by @total_ordering from (not a > b).'
147-
op_result = self.__gt__(other)
147+
op_result = type(self).__gt__(self, other)
148148
if op_result is NotImplemented:
149149
return op_result
150150
return not op_result
151151

152152
def _le_from_ge(self, other, NotImplemented=NotImplemented):
153153
'Return a <= b. Computed by @total_ordering from (not a >= b) or (a == b).'
154-
op_result = self.__ge__(other)
154+
op_result = type(self).__ge__(self, other)
155155
if op_result is NotImplemented:
156156
return op_result
157157
return not op_result or self == other
158158

159159
def _gt_from_ge(self, other, NotImplemented=NotImplemented):
160160
'Return a > b. Computed by @total_ordering from (a >= b) and (a != b).'
161-
op_result = self.__ge__(other)
161+
op_result = type(self).__ge__(self, other)
162162
if op_result is NotImplemented:
163163
return op_result
164164
return op_result and self != other
165165

166166
def _lt_from_ge(self, other, NotImplemented=NotImplemented):
167167
'Return a < b. Computed by @total_ordering from (not a >= b).'
168-
op_result = self.__ge__(other)
168+
op_result = type(self).__ge__(self, other)
169169
if op_result is NotImplemented:
170170
return op_result
171171
return not op_result

Lib/test/test_functools.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,34 @@ def test_pickle(self):
11631163
method_copy = pickle.loads(pickle.dumps(method, proto))
11641164
self.assertIs(method_copy, method)
11651165

1166+
1167+
def test_total_ordering_for_metaclasses_issue_44605(self):
1168+
1169+
@functools.total_ordering
1170+
class SortableMeta(type):
1171+
def __new__(cls, name, bases, ns):
1172+
return super().__new__(cls, name, bases, ns)
1173+
1174+
def __lt__(self, other):
1175+
if not isinstance(other, SortableMeta):
1176+
pass
1177+
return self.__name__ < other.__name__
1178+
1179+
def __eq__(self, other):
1180+
if not isinstance(other, SortableMeta):
1181+
pass
1182+
return self.__name__ == other.__name__
1183+
1184+
class B(metaclass=SortableMeta):
1185+
pass
1186+
1187+
class A(metaclass=SortableMeta):
1188+
pass
1189+
1190+
self.assertTrue(A < B)
1191+
self.assertFalse(A > B)
1192+
1193+
11661194
@functools.total_ordering
11671195
class Orderable_LT:
11681196
def __init__(self, value):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
The @functools.total_ordering() decorator now works with metaclasses.

0 commit comments

Comments
 (0)