Skip to content

Commit fb3dc22

Browse files
deal with overridden getattr etc.
1 parent f55d7d5 commit fb3dc22

File tree

2 files changed

+41
-13
lines changed

2 files changed

+41
-13
lines changed

bpython/simpleeval.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@
3030

3131
import ast
3232
from six import string_types
33+
import inspect
3334
from six.moves import builtins
3435

3536
from bpython import line as line_properties
3637
from bpython._py3compat import py3
37-
from bpython.inspection import is_new_style
38+
from bpython.inspection import is_new_style, AttrCleaner
3839

3940
_string_type_nodes = (ast.Str, ast.Bytes) if py3 else (ast.Str,)
4041
_numeric_types = (int, float, complex) + (() if py3 else (long,))
@@ -239,11 +240,12 @@ def evaluate_current_attribute(cursor_offset, line, namespace=None):
239240
def safe_get_attribute(obj, attr):
240241
"""Gets attributes without triggering descriptors on new-style clases"""
241242
if is_new_style(obj):
242-
result = safe_get_attribute_new_style(obj, attr)
243-
if isinstance(result, member_descriptor):
244-
# will either be the same slot descriptor or the value
245-
return getattr(obj, attr)
246-
return result
243+
with AttrCleaner(obj):
244+
result = safe_get_attribute_new_style(obj, attr)
245+
if isinstance(result, member_descriptor):
246+
# will either be the same slot descriptor or the value
247+
return getattr(obj, attr)
248+
return result
247249
return getattr(obj, attr)
248250

249251

@@ -264,16 +266,12 @@ def safe_get_attribute_new_style(obj, attr):
264266
"""
265267
if not is_new_style(obj):
266268
raise ValueError("%r is not a new-style class or object" % obj)
267-
to_look_through = (obj.mro()
268-
if hasattr(obj, 'mro')
269-
else [obj] + type(obj).mro())
269+
to_look_through = (obj.__mro__
270+
if inspect.isclass(obj)
271+
else (obj,) + type(obj).__mro__)
270272

271-
found_in_slots = hasattr(obj, '__slots__') and attr in obj.__slots__
272273
for cls in to_look_through:
273274
if hasattr(cls, '__dict__') and attr in cls.__dict__:
274275
return cls.__dict__[attr]
275276

276-
if found_in_slots:
277-
return AttributeIsEmptySlot
278-
279277
raise AttributeError()

bpython/test/test_simpleeval.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,24 @@ def s4(self):
162162
raise AssertionError('Property __get__ executed')
163163

164164

165+
class OverridenGetattr(object):
166+
def __getattr__(self, attr):
167+
raise AssertionError('custom __getattr__ executed')
168+
a = 1
169+
170+
171+
class OverridenGetattribute(object):
172+
def __getattribute__(self, attr):
173+
raise AssertionError('custom __getattrribute__ executed')
174+
a = 1
175+
176+
177+
class OverridenMRO(object):
178+
def __mro__(self):
179+
raise AssertionError('custom mro executed')
180+
a = 1
181+
182+
165183
member_descriptor = type(Slots.s1)
166184

167185

@@ -218,5 +236,17 @@ def test_lookup_with_property_and_slots(self):
218236
Slots.__dict__['s3'])
219237
self.assertIsInstance(sga(SlotsSubclass, 's3'), property)
220238

239+
def test_lookup_on_overriden_methods(self):
240+
sga = safe_get_attribute
241+
self.assertEqual(sga(OverridenGetattr(), 'a'), 1)
242+
self.assertEqual(sga(OverridenGetattribute(), 'a'), 1)
243+
self.assertEqual(sga(OverridenMRO(), 'a'), 1)
244+
with self.assertRaises(AttributeError):
245+
sga(OverridenGetattr(), 'b')
246+
with self.assertRaises(AttributeError):
247+
sga(OverridenGetattribute(), 'b')
248+
with self.assertRaises(AttributeError):
249+
sga(OverridenMRO(), 'b')
250+
221251
if __name__ == '__main__':
222252
unittest.main()

0 commit comments

Comments
 (0)