From e8feaa8d3a82da14cfb54a0000d3cfa1c1958ea4 2016-01-11 11:45:08 From: Thomas Kluyver Date: 2016-01-11 11:45:08 Subject: [PATCH] Add test for infinite looping on attribute access --- diff --git a/IPython/core/tests/test_oinspect.py b/IPython/core/tests/test_oinspect.py index e868730..c625a74 100644 --- a/IPython/core/tests/test_oinspect.py +++ b/IPython/core/tests/test_oinspect.py @@ -186,6 +186,23 @@ class NoBoolCall: raise NotImplementedError('Must be implemented') +class SerialLiar(object): + """Attribute accesses always get another copy of the same class. + + unittest.mock.call does something similar, but it's not ideal for testing + as the failure mode is to eat all your RAM. This gives up after 10k levels. + """ + def __init__(self, max_fibbing_twig, lies_told=0): + if lies_told > 10000: + raise RuntimeError('Nose too long, honesty is the best policy') + self.max_fibbing_twig = max_fibbing_twig + self.lies_told = lies_told + max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told) + + def __getattr__(self, item): + return SerialLiar(self.max_fibbing_twig, self.lies_told + 1) + + def check_calltip(obj, name, call, docstring): """Generic check pattern all calltip tests will use""" info = inspector.info(obj, name) @@ -297,6 +314,14 @@ def test_info_awkward(): def test_bool_raise(): inspector.info(NoBoolCall()) +def test_info_serialliar(): + fib_tracker = [0] + i = inspector.info(SerialLiar(fib_tracker)) + + # Nested attribute access should be cut off at 100 levels deep to avoid + # infinite loops: https://github.com/ipython/ipython/issues/9122 + nt.assert_less(fib_tracker[0], 9000) + def test_calldef_none(): # We should ignore __call__ for all of these. for obj in [f, SimpleClass().method, any, str.upper]: