Skip to content

Commit 15585b0

Browse files
authored
Merge pull request #214 from weka-io/better-aliasing-exceptions
Improve `RecursionError` for aliasing-induced infinite recursion
2 parents c42c2c8 + 295076f commit 15585b0

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- units: DataSize - return 'byte' and 'MiB' instead of '1 byte' and '1 MiB'
1313
- units: Duration(inf) is now 'Eternity' instead of 'Never'
1414
- timing: The Timer class now renders the duration using the Duration's repr, instead of as a float
15+
- aliasing: Improved `RecursionError` for aliasing-induced infinite recursion
1516

1617
### Added
1718
- CLI: expose 'colorize' and 'ziplog' as clis

easypy/aliasing.py

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ def super_dir(obj):
1313
return sorted(set(chain(dir(type(obj)), obj.__dict__)))
1414

1515

16+
# Python 3.4 does not have RecursionError - it throws a RuntimeError instead
17+
try:
18+
_RecursionError = RecursionError
19+
except NameError:
20+
_RecursionError = RuntimeError
21+
22+
1623
class AliasingMixin():
1724
@property
1825
def _aliased(self):
@@ -29,7 +36,16 @@ def __dir__(self):
2936
def __getattr__(self, attr):
3037
if attr.startswith("_"):
3138
raise AttributeError(attr)
32-
return getattr(self._aliased, attr)
39+
try:
40+
return getattr(self._aliased, attr)
41+
except _RecursionError as e:
42+
if type(e) is RuntimeError and str(e) != 'maximum recursion depth exceeded':
43+
raise
44+
raise _RecursionError('Infinite recursion trying to access {attr!r} on {obj!r} (via {type_name}.{alias}.{attr})'.format(
45+
attr=attr,
46+
obj=self,
47+
type_name=type(self).__name__,
48+
alias=self._ALIAS))
3349

3450

3551
def aliases(name, static=True):

tests/test_aliasing.py

+21
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,24 @@ def __init__(self, x):
4141
f = Foo("5")
4242
assert f.get("a") == 1
4343
assert f == 5
44+
45+
46+
def test_aliasing_infinite_recursion_exception():
47+
@aliases('bar', static=False)
48+
class Foo:
49+
def __init__(self):
50+
self.bar = Bar(self)
51+
52+
def __repr__(self):
53+
return 'Foo()'
54+
55+
class Bar:
56+
def __init__(self, foo):
57+
self.foo = foo
58+
59+
def baz(self):
60+
return self.foo.baz()
61+
62+
with pytest.raises(getattr(__builtins__, 'RecursionError', RuntimeError)) as e:
63+
Foo().baz()
64+
assert str(e.value) == "Infinite recursion trying to access 'baz' on Foo() (via Foo.bar.baz)"

0 commit comments

Comments
 (0)