Skip to content

Commit d410c8c

Browse files
committed
Add test coverage for has_attr_safe
1 parent 2ca6b7c commit d410c8c

File tree

4 files changed

+38
-26
lines changed

4 files changed

+38
-26
lines changed

bpython/autocomplete.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,6 @@ def attr_matches(self, text, namespace):
340340
"""Taken from rlcompleter.py and bent to my will.
341341
"""
342342

343-
# Gna, Py 2.6's rlcompleter searches for __call__ inside the
344-
# instance instead of the type, so we monkeypatch to prevent
345-
# side-effects (__getattr__/__getattribute__)
346343
m = self.attr_matches_re.match(text)
347344
if not m:
348345
return []
@@ -364,7 +361,7 @@ def attr_lookup(self, obj, expr, attr):
364361
be wrapped in a safe try/finally block in case anything bad happens to
365362
restore the original __getattribute__ method."""
366363
words = self.list_attributes(obj)
367-
if inspection.has_attr_safe(obj, "__class__"):
364+
if inspection.hasattr_safe(obj, "__class__"):
368365
words.append("__class__")
369366
words = words + rlcompleter.get_class_members(obj.__class__)
370367
if not isinstance(obj.__class__, abc.ABCMeta):
@@ -537,7 +534,7 @@ def matches(self, cursor_offset, line, **kwargs):
537534
except EvaluationError:
538535
return set()
539536

540-
# strips leading dot
537+
# strips leading dot
541538
matches = [m[1:] for m in self.attr_lookup(obj, "", attr.word)]
542539
return set(m for m in matches if few_enough_underscores(attr.word, m))
543540

@@ -677,7 +674,6 @@ def get_completer_bpython(cursor_offset, line, **kwargs):
677674

678675
def _callable_postfix(value, word):
679676
"""rlcompleter's _callable_postfix done right."""
680-
# TODO: do we need this done within an AttrCleaner?
681677
if inspection.is_callable(value):
682678
word += "("
683679
return word

bpython/inspection.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ def getpydocspec(f, func):
215215
if s is None:
216216
return None
217217

218-
if not has_attr_safe(f, "__name__") or s.groups()[0] != f.__name__:
218+
if not hasattr_safe(f, "__name__") or s.groups()[0] != f.__name__:
219219
return None
220220

221221
args = list()
@@ -394,7 +394,7 @@ def get_encoding_file(fname):
394394

395395
if not py3:
396396

397-
def get_attr_safe(obj, name):
397+
def getattr_safe(obj, name):
398398
"""side effect free getattr"""
399399
if not is_new_style(obj):
400400
return getattr(obj, name)
@@ -416,18 +416,18 @@ def get_attr_safe(obj, name):
416416

417417
else:
418418

419-
def get_attr_safe(obj, name):
420-
"""side effect and AttrCleaner free getattr (calls getattr_static)."""
419+
def getattr_safe(obj, name):
420+
"""side effect free getattr (calls getattr_static)."""
421421
result = inspect.getattr_static(obj, name)
422422
# Slots are a MemberDescriptorType
423423
if isinstance(result, MemberDescriptorType):
424424
result = getattr(obj, name)
425425
return result
426426

427427

428-
def has_attr_safe(obj, name):
428+
def hasattr_safe(obj, name):
429429
try:
430-
get_attr_safe(obj, name)
430+
getattr_safe(obj, name)
431431
return True
432432
except AttributeError:
433433
return False

bpython/simpleeval.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
from . import line as line_properties
4040
from ._py3compat import py3
41-
from .inspection import get_attr_safe
41+
from .inspection import getattr_safe
4242

4343
_string_type_nodes = (ast.Str, ast.Bytes) if py3 else (ast.Str,)
4444
_numeric_types = (int, float, complex) + (() if py3 else (long,))
@@ -163,7 +163,7 @@ def _convert(node):
163163
if isinstance(node, ast.Attribute):
164164
obj = _convert(node.value)
165165
attr = node.attr
166-
return get_attr_safe(obj, attr)
166+
return getattr_safe(obj, attr)
167167

168168
raise ValueError("malformed string")
169169

bpython/test/test_inspection.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -159,43 +159,53 @@ class TestSafeGetAttribute(unittest.TestCase):
159159
def test_lookup_on_object(self):
160160
a = A()
161161
a.x = 1
162-
self.assertEqual(get_attr_safe(a, "x"), 1)
163-
self.assertEqual(get_attr_safe(a, "a"), "a")
162+
self.assertEqual(getattr_safe(a, "x"), 1)
163+
self.assertEqual(getattr_safe(a, "a"), "a")
164164
b = B()
165165
b.y = 2
166-
self.assertEqual(get_attr_safe(b, "y"), 2)
167-
self.assertEqual(get_attr_safe(b, "a"), "a")
168-
self.assertEqual(get_attr_safe(b, "b"), "b")
166+
self.assertEqual(getattr_safe(b, "y"), 2)
167+
self.assertEqual(getattr_safe(b, "a"), "a")
168+
self.assertEqual(getattr_safe(b, "b"), "b")
169+
170+
self.assertEqual(hasattr_safe(b, "y"), True)
171+
self.assertEqual(hasattr_safe(b, "b"), True)
172+
169173

170174
def test_avoid_running_properties(self):
171175
p = Property()
172-
self.assertEqual(get_attr_safe(p, "prop"), Property.prop)
176+
self.assertEqual(getattr_safe(p, "prop"), Property.prop)
177+
self.assertEqual(hasattr_safe(p, "prop"), True)
173178

174179
def test_lookup_with_slots(self):
175180
s = Slots()
176181
s.s1 = "s1"
177-
self.assertEqual(get_attr_safe(s, "s1"), "s1")
182+
self.assertEqual(getattr_safe(s, "s1"), "s1")
178183
with self.assertRaises(AttributeError):
179-
get_attr_safe(s, "s2")
184+
getattr_safe(s, "s2")
185+
186+
self.assertEqual(hasattr_safe(s, "s2"), False)
180187

181188
def test_lookup_on_slots_classes(self):
182-
sga = get_attr_safe
189+
sga = getattr_safe
183190
s = SlotsSubclass()
184191
self.assertIsInstance(sga(Slots, "s1"), member_descriptor)
185192
self.assertIsInstance(sga(SlotsSubclass, "s1"), member_descriptor)
186193
self.assertIsInstance(sga(SlotsSubclass, "s4"), property)
187194
self.assertIsInstance(sga(s, "s4"), property)
188195

196+
self.assertEqual(hasattr_safe(s, "s1"), True)
197+
self.assertEqual(hasattr_safe(s, "s4"), True)
198+
189199
@unittest.skipIf(py3, "Py 3 doesn't allow slots and prop in same class")
190200
def test_lookup_with_property_and_slots(self):
191-
sga = get_attr_safe
201+
sga = getattr_safe
192202
s = SlotsSubclass()
193203
self.assertIsInstance(sga(Slots, "s3"), property)
194-
self.assertEqual(get_attr_safe(s, "s3"), Slots.__dict__["s3"])
204+
self.assertEqual(getattr_safe(s, "s3"), Slots.__dict__["s3"])
195205
self.assertIsInstance(sga(SlotsSubclass, "s3"), property)
196206

197207
def test_lookup_on_overridden_methods(self):
198-
sga = get_attr_safe
208+
sga = getattr_safe
199209
self.assertEqual(sga(OverriddenGetattr(), "a"), 1)
200210
self.assertEqual(sga(OverriddenGetattribute(), "a"), 1)
201211
self.assertEqual(sga(OverriddenMRO(), "a"), 1)
@@ -206,6 +216,12 @@ def test_lookup_on_overridden_methods(self):
206216
with self.assertRaises(AttributeError):
207217
sga(OverriddenMRO(), "b")
208218

219+
self.assertEqual(hasattr_safe(OverriddenGetattr(), "b"), False)
220+
self.assertEqual(hasattr_safe(OverriddenGetattribute(), "b"), False)
221+
self.assertEqual(hasattr_safe(OverriddenMRO(), "b"), False)
222+
223+
224+
209225

210226
if __name__ == "__main__":
211227
unittest.main()

0 commit comments

Comments
 (0)