Skip to content

Commit 645df4c

Browse files
committed
make callable detection use hasattr(obj, '__call__') wrapped in dark magic
1 parent 1f4853d commit 645df4c

File tree

1 file changed

+33
-17
lines changed

1 file changed

+33
-17
lines changed

bpython/cli.py

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -326,15 +326,10 @@ def __init__(self, scr, interp, statusbar=None, idle=None):
326326
'ignore') as hfile:
327327
self.rl_hist = hfile.readlines()
328328

329-
def attr_matches(self, text):
330-
"""Taken from rlcompleter.py and bent to my will."""
331-
332-
m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
333-
if not m:
334-
return []
335-
336-
expr, attr = m.group(1, 3)
337-
obj = eval(expr, self.interp.locals)
329+
def clean_object(self, obj):
330+
"""Try to make an object not exhibit side-effects on attribute
331+
lookup. Return the type's magic attributes so they can be reapplied
332+
with restore_object"""
338333
type_ = type(obj)
339334
__getattribute__ = None
340335
__getattr__ = None
@@ -362,16 +357,33 @@ def attr_matches(self, text):
362357
# XXX: This happens for e.g. built-in types
363358
__getattribute__ = None
364359
# /Dark magic
360+
return __getattribute__, __getattr__
361+
362+
def restore_object(self, obj, attribs):
363+
"""Restore an object's magic methods as returned from clean_object"""
364+
type_ = type(obj)
365+
__getattribute__, __getattr__ = attribs
366+
# Dark magic:
367+
if __getattribute__ is not None:
368+
setattr(type_, '__getattribute__', __getattribute__)
369+
if __getattr__ is not None:
370+
setattr(type_, '__getattr__', __getattr__)
371+
# /Dark magic
365372

373+
def attr_matches(self, text):
374+
"""Taken from rlcompleter.py and bent to my will."""
375+
376+
m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
377+
if not m:
378+
return []
379+
380+
expr, attr = m.group(1, 3)
381+
obj = eval(expr, self.interp.locals)
382+
attribs = self.clean_object(obj)
366383
try:
367384
matches = self.attr_lookup(obj, expr, attr)
368385
finally:
369-
# Dark magic:
370-
if __getattribute__ is not None:
371-
setattr(type_, '__getattribute__', __getattribute__)
372-
if __getattr__ is not None:
373-
setattr(type_, '__getattr__', __getattr__)
374-
# /Dark magic
386+
self.restore_object(obj, attribs)
375387
return matches
376388

377389
def attr_lookup(self, obj, expr, attr):
@@ -392,8 +404,12 @@ def attr_lookup(self, obj, expr, attr):
392404

393405
def _callable_postfix(self, value, word):
394406
"""rlcompleter's _callable_postfix done right."""
395-
if hasattr(type(value), '__call__'):
396-
word += '('
407+
attribs = self.clean_object(value)
408+
try:
409+
if hasattr(value, '__call__'):
410+
word += '('
411+
finally:
412+
self.restore_object(value, attribs)
397413
return word
398414

399415
def cw(self):

0 commit comments

Comments
 (0)