commit: a9d9aabc4499b26523456a08e5013e6d0f9bc4e1
Author: Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sat Jan 10 12:09:29 2026 +0000
Commit: Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Sat Jan 10 12:09:29 2026 +0000
URL:
https://gitweb.gentoo.org/proj/pkgcore/snakeoil.git/commit/?id=a9d9aabc
break tests import cycle on klass.alias_method
snakeoil.klass is heavily used within snakeoil. Deprecation
for example uses obj.Delayed, which in turn uses klass. Due
to klass exposing alias_method at snakeoil.klass.alias_method,
there is a hard cycle.
Break that cycle via moving alias_method out so it's not
part of the import chain.
Signed-off-by: Brian Harring <ferringb <AT> gmail.com>
src/snakeoil/_klass.py | 42 ++++++++++++++++++++++++++++++++++++++++
src/snakeoil/klass/__init__.py | 2 +-
src/snakeoil/klass/properties.py | 35 +--------------------------------
src/snakeoil/obj.py | 2 +-
4 files changed, 45 insertions(+), 36 deletions(-)
diff --git a/src/snakeoil/_klass.py b/src/snakeoil/_klass.py
new file mode 100644
index 0000000..3955403
--- /dev/null
+++ b/src/snakeoil/_klass.py
@@ -0,0 +1,42 @@
+"""
+functionality for snakeoil.klass, stored here for cycle breaking reasons
+
+Deprecating anything in snakeoil.klass tends to trigger a cycle due to the
registry
+implementation having to reuse parts of snakeoil.klass.
+"""
+
+import operator
+
+
+def alias_method(attr, name=None, doc=None):
+ """at runtime, redirect to another method
+
+ This is primarily useful for when compatibility, or a protocol requires
+ you to have the same functionality available at multiple spots- for example
+ :py:func:`dict.has_key` and :py:func:`dict.__contains__`.
+
+ :param attr: attribute to redirect to
+ :param name: ``__name__`` to force for the new method if desired
+ :param doc: ``__doc__`` to force for the new method if desired
+
+ >>> from snakeoil.klass import alias_method
+ >>> class foon:
+ ... def orig(self):
+ ... return 1
+ ... alias = alias_method("orig")
+ >>> obj = foon()
+ >>> assert obj.orig() == obj.alias()
+ >>> assert obj.alias() == 1
+ """
+ grab_attr = operator.attrgetter(attr)
+
+ def _asecond_level_call(self, *a, **kw):
+ return grab_attr(self)(*a, **kw)
+
+ if doc is None:
+ doc = f"Method alias to invoke :py:meth:`{attr}`."
+
+ _asecond_level_call.__doc__ = doc
+ if name:
+ _asecond_level_call.__name__ = name
+ return _asecond_level_call
diff --git a/src/snakeoil/klass/__init__.py b/src/snakeoil/klass/__init__.py
index 4603a08..e777c5e 100644
--- a/src/snakeoil/klass/__init__.py
+++ b/src/snakeoil/klass/__init__.py
@@ -46,6 +46,7 @@ from operator import attrgetter
from snakeoil._internals import deprecated
from snakeoil.sequences import unique_stable
+from .._klass import alias_method
from ..caching import WeakInstMeta
from ._deprecated import (
ImmutableInstance,
@@ -57,7 +58,6 @@ from ._deprecated import (
from .properties import (
_uncached_singleton, # noqa: F401 . This exists purely due to a stupid
usage of pkgcore.ebuild.profile which is being removed.
alias_attr,
- alias_method,
cached_property,
cached_property_named,
jit_attr,
diff --git a/src/snakeoil/klass/properties.py b/src/snakeoil/klass/properties.py
index dcd1852..cfe0866 100644
--- a/src/snakeoil/klass/properties.py
+++ b/src/snakeoil/klass/properties.py
@@ -12,6 +12,7 @@ __all__ = (
import operator
import typing
+from .._klass import alias_method
from ..currying import post_curry
@@ -270,37 +271,3 @@ def alias_attr(target_attr):
True
"""
return property(operator.attrgetter(target_attr), doc=f"alias to
{target_attr}")
-
-
-def alias_method(attr, name=None, doc=None):
- """at runtime, redirect to another method
-
- This is primarily useful for when compatibility, or a protocol requires
- you to have the same functionality available at multiple spots- for example
- :py:func:`dict.has_key` and :py:func:`dict.__contains__`.
-
- :param attr: attribute to redirect to
- :param name: ``__name__`` to force for the new method if desired
- :param doc: ``__doc__`` to force for the new method if desired
-
- >>> from snakeoil.klass import alias_method
- >>> class foon:
- ... def orig(self):
- ... return 1
- ... alias = alias_method("orig")
- >>> obj = foon()
- >>> assert obj.orig() == obj.alias()
- >>> assert obj.alias() == 1
- """
- grab_attr = operator.attrgetter(attr)
-
- def _asecond_level_call(self, *a, **kw):
- return grab_attr(self)(*a, **kw)
-
- if doc is None:
- doc = f"Method alias to invoke :py:meth:`{attr}`."
-
- _asecond_level_call.__doc__ = doc
- if name:
- _asecond_level_call.__name__ = name
- return _asecond_level_call
diff --git a/src/snakeoil/obj.py b/src/snakeoil/obj.py
index 060b522..6807011 100644
--- a/src/snakeoil/obj.py
+++ b/src/snakeoil/obj.py
@@ -77,7 +77,7 @@ __all__ = ("DelayedInstantiation", "make_kls")
import typing
-from .klass.properties import alias_method
+from ._klass import alias_method
# For our proxy, we have two sets of descriptors-
# common, "always there" descriptors that come from