Skip to content

Commit 01299fc

Browse files
committed
feat: deprecate inject_richcmp_from_cmp . Use either GenericRichComparison or total_ordering
GenericRichComparison is specialized against the underlying __attr_comparison__ logic, barring that, just use functools.total_ordering. I expect the main (sole?) user of GenericRichComparison will be CPV and atom classes, since pulling their __cmp__ usage apart will be a PITA. Signed-off-by: Brian Harring <ferringb@gmail.com>
1 parent 6404efc commit 01299fc

File tree

2 files changed

+89
-81
lines changed

2 files changed

+89
-81
lines changed

src/snakeoil/klass/__init__.py

Lines changed: 6 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@
4747
from snakeoil.deprecation import deprecated as warn_deprecated
4848

4949
from ..caching import WeakInstMeta
50-
from .deprecated import ImmutableInstance, immutable_instance, inject_immutable_instance
50+
from .deprecated import (
51+
ImmutableInstance,
52+
immutable_instance,
53+
inject_immutable_instance,
54+
inject_richcmp_methods_from_cmp,
55+
)
5156
from .properties import (
5257
_uncached_singleton, # noqa: F401 . This exists purely due to a stupid usage of pkgcore.ebuild.profile which is being removed.
5358
alias,
@@ -238,86 +243,6 @@ def generic_equality(
238243
return real_type(name, bases, scope)
239244

240245

241-
def generic_lt(self, other):
242-
"""generic implementation of __lt__ that uses __cmp__"""
243-
return self.__cmp__(other) < 0
244-
245-
246-
def generic_le(self, other):
247-
"""reflective implementation of __le__ that uses __cmp__"""
248-
return self.__cmp__(other) <= 0
249-
250-
251-
def generic_eq(self, other):
252-
"""reflective implementation of __eq__ that uses __cmp__"""
253-
return self.__cmp__(other) == 0
254-
255-
256-
def generic_ne(self, other):
257-
"""reflective implementation of __ne__ that uses __cmp__"""
258-
return self.__cmp__(other) != 0
259-
260-
261-
def generic_ge(self, other):
262-
"""reflective implementation of __ge__ that uses __cmp__"""
263-
return self.__cmp__(other) >= 0
264-
265-
266-
def generic_gt(self, other):
267-
"""reflective implementation of __gt__ that uses __cmp__"""
268-
return self.__cmp__(other) > 0
269-
270-
271-
def inject_richcmp_methods_from_cmp(scope):
272-
"""
273-
class namespace modifier injecting richcmp methods that rely on __cmp__ for py3k
274-
compatibility
275-
276-
Note that this just injects generic implementations such as :py:func:`generic_lt`;
277-
if a method already exists, it will not override it. This behavior is primarily
278-
beneficial if the developer wants to optimize one specific method- __lt__ for sorting
279-
reasons for example, but performance is less of a concern for the other
280-
rich comparison methods.
281-
282-
Example usage:
283-
284-
>>> from snakeoil.klass import inject_richcmp_methods_from_cmp
285-
>>> from snakeoil.compatibility import cmp
286-
>>> class foo:
287-
...
288-
... # note that for this example, we inject always since we're
289-
... # explicitly accessing __ge__ methods- under py2k, they wouldn't
290-
... # exist (__cmp__ would be sufficient).
291-
...
292-
... # add the generic rich comparsion methods to the local class namespace
293-
... inject_richcmp_methods_from_cmp(locals())
294-
...
295-
... def __init__(self, a, b):
296-
... self.a, self.b = a, b
297-
...
298-
... def __cmp__(self, other):
299-
... c = cmp(self.a, other.a)
300-
... if c == 0:
301-
... c = cmp(self.b, other.b)
302-
... return c
303-
>>>
304-
>>> assert foo(1, 2).__ge__(foo(1, 1))
305-
>>> assert foo(1, 1).__eq__(foo(1, 1))
306-
307-
:param scope: the modifiable scope of a class namespace to work on
308-
"""
309-
310-
for key, func in (
311-
("__lt__", generic_lt),
312-
("__le__", generic_le),
313-
("__eq__", generic_eq),
314-
("__ne__", generic_ne),
315-
("__ge__", generic_ge),
316-
("__gt__", generic_gt),
317-
):
318-
scope.setdefault(key, func)
319-
320-
321246
@warn_deprecated(
322247
"snakeoil.klass.chained_getter is deprecated. Use operator.attrgetter instead."
323248
)

src/snakeoil/klass/deprecated.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,86 @@ def inject_immutable_instance(scope: dict[str, typing.Any]):
5151
"""
5252
scope.setdefault("__setattr__", ImmutableInstance.__setattr__)
5353
scope.setdefault("__delattr__", ImmutableInstance.__delattr__)
54+
55+
56+
def __generic_lt(self, other):
57+
"""generic implementation of __lt__ that uses __cmp__"""
58+
return self.__cmp__(other) < 0
59+
60+
61+
def __generic_le(self, other):
62+
"""reflective implementation of __le__ that uses __cmp__"""
63+
return self.__cmp__(other) <= 0
64+
65+
66+
def __generic_eq(self, other):
67+
"""reflective implementation of __eq__ that uses __cmp__"""
68+
return self.__cmp__(other) == 0
69+
70+
71+
def __generic_ne(self, other):
72+
"""reflective implementation of __ne__ that uses __cmp__"""
73+
return self.__cmp__(other) != 0
74+
75+
76+
def __generic_ge(self, other):
77+
"""reflective implementation of __ge__ that uses __cmp__"""
78+
return self.__cmp__(other) >= 0
79+
80+
81+
def __generic_gt(self, other):
82+
"""reflective implementation of __gt__ that uses __cmp__"""
83+
return self.__cmp__(other) > 0
84+
85+
86+
@deprecated(
87+
"inject_richcmp_methods_from_cmp is deprecated, migrate to functools.total_ordering instead."
88+
)
89+
def inject_richcmp_methods_from_cmp(scope):
90+
"""
91+
class namespace modifier injecting richcmp methods that rely on __cmp__ for py3k
92+
compatibility
93+
94+
Note that this just injects generic implementations such as :py:func:`__generic_lt`;
95+
if a method already exists, it will not override it. This behavior is primarily
96+
beneficial if the developer wants to optimize one specific method- __lt__ for sorting
97+
reasons for example, but performance is less of a concern for the other
98+
rich comparison methods.
99+
100+
Example usage:
101+
102+
>>> from snakeoil.klass import inject_richcmp_methods_from_cmp
103+
>>> from snakeoil.compatibility import cmp
104+
>>> class foo:
105+
...
106+
... # note that for this example, we inject always since we're
107+
... # explicitly accessing __ge__ methods- under py2k, they wouldn't
108+
... # exist (__cmp__ would be sufficient).
109+
...
110+
... # add the generic rich comparsion methods to the local class namespace
111+
... inject_richcmp_methods_from_cmp(locals())
112+
...
113+
... def __init__(self, a, b):
114+
... self.a, self.b = a, b
115+
...
116+
... def __cmp__(self, other):
117+
... c = cmp(self.a, other.a)
118+
... if c == 0:
119+
... c = cmp(self.b, other.b)
120+
... return c
121+
>>>
122+
>>> assert foo(1, 2).__ge__(foo(1, 1))
123+
>>> assert foo(1, 1).__eq__(foo(1, 1))
124+
125+
:param scope: the modifiable scope of a class namespace to work on
126+
"""
127+
128+
for key, func in (
129+
("__lt__", __generic_lt),
130+
("__le__", __generic_le),
131+
("__eq__", __generic_eq),
132+
("__ne__", __generic_ne),
133+
("__ge__", __generic_ge),
134+
("__gt__", __generic_gt),
135+
):
136+
scope.setdefault(key, func)

0 commit comments

Comments
 (0)