diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py index a1dda16..808dbaf 100644 --- a/IPython/core/oinspect.py +++ b/IPython/core/oinspect.py @@ -261,6 +261,16 @@ def call_tip(oinfo, format_call=True): return call_line, doc +def safe_hasattr(obj, attr): + """In recent versions of Python, hasattr() only catches AttributeError. + This catches all errors. + """ + try: + getattr(obj, attr) + return True + except: + return False + def find_file(obj): """Find the absolute path to the file where an object was defined. @@ -279,7 +289,7 @@ def find_file(obj): The absolute path to the file where the object was defined. """ # get source if obj was decorated with @decorator - if hasattr(obj, '__wrapped__'): + if safe_hasattr(obj, '__wrapped__'): obj = obj.__wrapped__ fname = None @@ -316,7 +326,7 @@ def find_source_lines(obj): The line number where the object definition starts. """ # get source if obj was decorated with @decorator - if hasattr(obj, '__wrapped__'): + if safe_hasattr(obj, '__wrapped__'): obj = obj.__wrapped__ try: @@ -779,7 +789,7 @@ class Inspector: out['init_docstring'] = init_ds # Call form docstring for callable instances - if hasattr(obj, '__call__'): + if safe_hasattr(obj, '__call__'): call_def = self._getdef(obj.__call__, oname) if call_def is not None: out['call_def'] = self.format(call_def) diff --git a/IPython/core/tests/test_oinspect.py b/IPython/core/tests/test_oinspect.py index 2e5e13c..91346a3 100644 --- a/IPython/core/tests/test_oinspect.py +++ b/IPython/core/tests/test_oinspect.py @@ -158,6 +158,11 @@ class SimpleMagics(Magics): "A class-based line/cell magic" +class Awkward(object): + def __getattr__(self, name): + raise Exception(name) + + def check_calltip(obj, name, call, docstring): """Generic check pattern all calltip tests will use""" info = inspector.info(obj, name) @@ -261,6 +266,10 @@ def test_info(): nt.assert_equal(i['type_name'], 'instance') nt.assert_equal(i['docstring'], OldStyle.__doc__) +def test_info_awkward(): + # Just test that this doesn't throw an error. + i = inspector.info(Awkward()) + def test_getdoc(): class A(object): """standard docstring"""