Skip to content

Commit a9d9aab

Browse files
committed
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@gmail.com>
1 parent cd8fbdb commit a9d9aab

File tree

4 files changed

+45
-36
lines changed

4 files changed

+45
-36
lines changed

src/snakeoil/_klass.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
functionality for snakeoil.klass, stored here for cycle breaking reasons
3+
4+
Deprecating anything in snakeoil.klass tends to trigger a cycle due to the registry
5+
implementation having to reuse parts of snakeoil.klass.
6+
"""
7+
8+
import operator
9+
10+
11+
def alias_method(attr, name=None, doc=None):
12+
"""at runtime, redirect to another method
13+
14+
This is primarily useful for when compatibility, or a protocol requires
15+
you to have the same functionality available at multiple spots- for example
16+
:py:func:`dict.has_key` and :py:func:`dict.__contains__`.
17+
18+
:param attr: attribute to redirect to
19+
:param name: ``__name__`` to force for the new method if desired
20+
:param doc: ``__doc__`` to force for the new method if desired
21+
22+
>>> from snakeoil.klass import alias_method
23+
>>> class foon:
24+
... def orig(self):
25+
... return 1
26+
... alias = alias_method("orig")
27+
>>> obj = foon()
28+
>>> assert obj.orig() == obj.alias()
29+
>>> assert obj.alias() == 1
30+
"""
31+
grab_attr = operator.attrgetter(attr)
32+
33+
def _asecond_level_call(self, *a, **kw):
34+
return grab_attr(self)(*a, **kw)
35+
36+
if doc is None:
37+
doc = f"Method alias to invoke :py:meth:`{attr}`."
38+
39+
_asecond_level_call.__doc__ = doc
40+
if name:
41+
_asecond_level_call.__name__ = name
42+
return _asecond_level_call

src/snakeoil/klass/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
from snakeoil._internals import deprecated
4747
from snakeoil.sequences import unique_stable
4848

49+
from .._klass import alias_method
4950
from ..caching import WeakInstMeta
5051
from ._deprecated import (
5152
ImmutableInstance,
@@ -57,7 +58,6 @@
5758
from .properties import (
5859
_uncached_singleton, # noqa: F401 . This exists purely due to a stupid usage of pkgcore.ebuild.profile which is being removed.
5960
alias_attr,
60-
alias_method,
6161
cached_property,
6262
cached_property_named,
6363
jit_attr,

src/snakeoil/klass/properties.py

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import operator
1313
import typing
1414

15+
from .._klass import alias_method
1516
from ..currying import post_curry
1617

1718

@@ -270,37 +271,3 @@ def alias_attr(target_attr):
270271
True
271272
"""
272273
return property(operator.attrgetter(target_attr), doc=f"alias to {target_attr}")
273-
274-
275-
def alias_method(attr, name=None, doc=None):
276-
"""at runtime, redirect to another method
277-
278-
This is primarily useful for when compatibility, or a protocol requires
279-
you to have the same functionality available at multiple spots- for example
280-
:py:func:`dict.has_key` and :py:func:`dict.__contains__`.
281-
282-
:param attr: attribute to redirect to
283-
:param name: ``__name__`` to force for the new method if desired
284-
:param doc: ``__doc__`` to force for the new method if desired
285-
286-
>>> from snakeoil.klass import alias_method
287-
>>> class foon:
288-
... def orig(self):
289-
... return 1
290-
... alias = alias_method("orig")
291-
>>> obj = foon()
292-
>>> assert obj.orig() == obj.alias()
293-
>>> assert obj.alias() == 1
294-
"""
295-
grab_attr = operator.attrgetter(attr)
296-
297-
def _asecond_level_call(self, *a, **kw):
298-
return grab_attr(self)(*a, **kw)
299-
300-
if doc is None:
301-
doc = f"Method alias to invoke :py:meth:`{attr}`."
302-
303-
_asecond_level_call.__doc__ = doc
304-
if name:
305-
_asecond_level_call.__name__ = name
306-
return _asecond_level_call

src/snakeoil/obj.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777

7878
import typing
7979

80-
from .klass.properties import alias_method
80+
from ._klass import alias_method
8181

8282
# For our proxy, we have two sets of descriptors-
8383
# common, "always there" descriptors that come from

0 commit comments

Comments
 (0)