commit: dfd645f6be262d422d9be67c4b8cd408cf627ec5
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Mon Oct 27 13:20:25 2025 +0000
Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Mon Oct 27 21:38:37 2025 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=dfd645f6
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 <AT> gmail.com>
src/snakeoil/klass/__init__.py | 87 +++-------------------------------------
src/snakeoil/klass/deprecated.py | 83 ++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 81 deletions(-)
diff --git a/src/snakeoil/klass/__init__.py b/src/snakeoil/klass/__init__.py
index af3e007..9691dd9 100644
--- a/src/snakeoil/klass/__init__.py
+++ b/src/snakeoil/klass/__init__.py
@@ -47,7 +47,12 @@ from typing import Any
from snakeoil.deprecation import deprecated as warn_deprecated
from ..caching import WeakInstMeta
-from .deprecated import ImmutableInstance, immutable_instance,
inject_immutable_instance
+from .deprecated import (
+ ImmutableInstance,
+ immutable_instance,
+ inject_immutable_instance,
+ inject_richcmp_methods_from_cmp,
+)
from .properties import (
_uncached_singleton, # noqa: F401 . This exists purely due to a stupid
usage of pkgcore.ebuild.profile which is being removed.
alias,
@@ -238,86 +243,6 @@ def generic_equality(
return real_type(name, bases, scope)
-def generic_lt(self, other):
- """generic implementation of __lt__ that uses __cmp__"""
- return self.__cmp__(other) < 0
-
-
-def generic_le(self, other):
- """reflective implementation of __le__ that uses __cmp__"""
- return self.__cmp__(other) <= 0
-
-
-def generic_eq(self, other):
- """reflective implementation of __eq__ that uses __cmp__"""
- return self.__cmp__(other) == 0
-
-
-def generic_ne(self, other):
- """reflective implementation of __ne__ that uses __cmp__"""
- return self.__cmp__(other) != 0
-
-
-def generic_ge(self, other):
- """reflective implementation of __ge__ that uses __cmp__"""
- return self.__cmp__(other) >= 0
-
-
-def generic_gt(self, other):
- """reflective implementation of __gt__ that uses __cmp__"""
- return self.__cmp__(other) > 0
-
-
-def inject_richcmp_methods_from_cmp(scope):
- """
- class namespace modifier injecting richcmp methods that rely on __cmp__
for py3k
- compatibility
-
- Note that this just injects generic implementations such as
:py:func:`generic_lt`;
- if a method already exists, it will not override it. This behavior is
primarily
- beneficial if the developer wants to optimize one specific method- __lt__
for sorting
- reasons for example, but performance is less of a concern for the other
- rich comparison methods.
-
- Example usage:
-
- >>> from snakeoil.klass import inject_richcmp_methods_from_cmp
- >>> from snakeoil.compatibility import cmp
- >>> class foo:
- ...
- ... # note that for this example, we inject always since we're
- ... # explicitly accessing __ge__ methods- under py2k, they wouldn't
- ... # exist (__cmp__ would be sufficient).
- ...
- ... # add the generic rich comparsion methods to the local class
namespace
- ... inject_richcmp_methods_from_cmp(locals())
- ...
- ... def __init__(self, a, b):
- ... self.a, self.b = a, b
- ...
- ... def __cmp__(self, other):
- ... c = cmp(self.a, other.a)
- ... if c == 0:
- ... c = cmp(self.b, other.b)
- ... return c
- >>>
- >>> assert foo(1, 2).__ge__(foo(1, 1))
- >>> assert foo(1, 1).__eq__(foo(1, 1))
-
- :param scope: the modifiable scope of a class namespace to work on
- """
-
- for key, func in (
- ("__lt__", generic_lt),
- ("__le__", generic_le),
- ("__eq__", generic_eq),
- ("__ne__", generic_ne),
- ("__ge__", generic_ge),
- ("__gt__", generic_gt),
- ):
- scope.setdefault(key, func)
-
-
@warn_deprecated(
"snakeoil.klass.chained_getter is deprecated. Use operator.attrgetter
instead."
)
diff --git a/src/snakeoil/klass/deprecated.py b/src/snakeoil/klass/deprecated.py
index cb7811a..9c9d5a1 100644
--- a/src/snakeoil/klass/deprecated.py
+++ b/src/snakeoil/klass/deprecated.py
@@ -51,3 +51,86 @@ def inject_immutable_instance(scope: dict[str, typing.Any]):
"""
scope.setdefault("__setattr__", ImmutableInstance.__setattr__)
scope.setdefault("__delattr__", ImmutableInstance.__delattr__)
+
+
+def __generic_lt(self, other):
+ """generic implementation of __lt__ that uses __cmp__"""
+ return self.__cmp__(other) < 0
+
+
+def __generic_le(self, other):
+ """reflective implementation of __le__ that uses __cmp__"""
+ return self.__cmp__(other) <= 0
+
+
+def __generic_eq(self, other):
+ """reflective implementation of __eq__ that uses __cmp__"""
+ return self.__cmp__(other) == 0
+
+
+def __generic_ne(self, other):
+ """reflective implementation of __ne__ that uses __cmp__"""
+ return self.__cmp__(other) != 0
+
+
+def __generic_ge(self, other):
+ """reflective implementation of __ge__ that uses __cmp__"""
+ return self.__cmp__(other) >= 0
+
+
+def __generic_gt(self, other):
+ """reflective implementation of __gt__ that uses __cmp__"""
+ return self.__cmp__(other) > 0
+
+
+@deprecated(
+ "inject_richcmp_methods_from_cmp is deprecated, migrate to
functools.total_ordering instead."
+)
+def inject_richcmp_methods_from_cmp(scope):
+ """
+ class namespace modifier injecting richcmp methods that rely on __cmp__
for py3k
+ compatibility
+
+ Note that this just injects generic implementations such as
:py:func:`__generic_lt`;
+ if a method already exists, it will not override it. This behavior is
primarily
+ beneficial if the developer wants to optimize one specific method- __lt__
for sorting
+ reasons for example, but performance is less of a concern for the other
+ rich comparison methods.
+
+ Example usage:
+
+ >>> from snakeoil.klass import inject_richcmp_methods_from_cmp
+ >>> from snakeoil.compatibility import cmp
+ >>> class foo:
+ ...
+ ... # note that for this example, we inject always since we're
+ ... # explicitly accessing __ge__ methods- under py2k, they wouldn't
+ ... # exist (__cmp__ would be sufficient).
+ ...
+ ... # add the generic rich comparsion methods to the local class
namespace
+ ... inject_richcmp_methods_from_cmp(locals())
+ ...
+ ... def __init__(self, a, b):
+ ... self.a, self.b = a, b
+ ...
+ ... def __cmp__(self, other):
+ ... c = cmp(self.a, other.a)
+ ... if c == 0:
+ ... c = cmp(self.b, other.b)
+ ... return c
+ >>>
+ >>> assert foo(1, 2).__ge__(foo(1, 1))
+ >>> assert foo(1, 1).__eq__(foo(1, 1))
+
+ :param scope: the modifiable scope of a class namespace to work on
+ """
+
+ for key, func in (
+ ("__lt__", __generic_lt),
+ ("__le__", __generic_le),
+ ("__eq__", __generic_eq),
+ ("__ne__", __generic_ne),
+ ("__ge__", __generic_ge),
+ ("__gt__", __generic_gt),
+ ):
+ scope.setdefault(key, func)