inspect: return defargs[0] if obj.__dict__ raises exception

The fallback implemented in #2731 cannot return `obj.__dict__[name]`
if the `__dict__` method has been redefined in such a way that it
raises an exception when trying to access it.

This commit adds a try-except block to work around this.
This commit is contained in:
Leo Huckvale 2016-07-14 14:04:33 +01:00
parent 1dfa1a8d1e
commit 670049c262
2 changed files with 70 additions and 1 deletions

View File

@ -110,12 +110,23 @@ def safe_getattr(obj, name, *defargs):
except Exception:
# sometimes accessing a property raises an exception (e.g.
# NotImplementedError), so let's try to read the attribute directly
if name in obj.__dict__:
# We should also be aware that if `__dict__` has been overridden,
# this may also raise an exception.
try:
obj_dict = obj.__dict__
except Exception as exc:
obj_dict = {}
if name in obj_dict:
return obj.__dict__[name]
# this is a catch-all for all the weird things that some modules do
# with attribute access
if defargs:
return defargs[0]
raise AttributeError(name)

View File

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
"""
test_util_inspect
~~~~~~~~~~~~~~~
Tests util.inspect functions.
:copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from unittest import TestCase
from sphinx.util import inspect
class TestSafeGetAttr(TestCase):
def test_safe_getattr_with_default(self):
class Foo(object):
def __getattr__(self, item):
raise Exception
obj = Foo()
result = inspect.safe_getattr(obj, 'bar', 'baz')
assert result == 'baz'
def test_safe_getattr_with_exception(self):
class Foo(object):
def __getattr__(self, item):
raise Exception
obj = Foo()
with self.assertRaisesRegexp(AttributeError, 'bar'):
inspect.safe_getattr(obj, 'bar')
def test_safe_getattr_with_property_exception(self):
class Foo(object):
@property
def bar(self):
raise Exception
obj = Foo()
with self.assertRaisesRegexp(AttributeError, 'bar'):
inspect.safe_getattr(obj, 'bar')
def test_safe_getattr_with___dict___override(self):
class Foo(object):
@property
def __dict__(self):
raise Exception
obj = Foo()
with self.assertRaisesRegexp(AttributeError, 'bar'):
inspect.safe_getattr(obj, 'bar')