@@ -252,6 +252,54 @@ def writetb(self, l):
252252 fancy."""
253253 map (self .write , ["\x01 %s\x03 %s" % (OPTS .color_scheme ['error' ], i ) for i in l ])
254254
255+ class AttrCleaner (object ):
256+ """A context manager that tries to make an object not exhibit side-effects
257+ on attribute lookup."""
258+ def __init__ (self , obj ):
259+ self .obj = obj
260+
261+ def __enter__ (self ):
262+ """Try to make an object not exhibit side-effects on attribute
263+ lookup."""
264+ type_ = type (self .obj )
265+ __getattribute__ = None
266+ __getattr__ = None
267+ # Dark magic:
268+ # If __getattribute__ doesn't exist on the class and __getattr__ does
269+ # then __getattr__ will be called when doing
270+ # getattr(type_, '__getattribute__', None)
271+ # so we need to first remove the __getattr__, then the
272+ # __getattribute__, then look up the attributes and then restore the
273+ # original methods. :-(
274+ # The upshot being that introspecting on an object to display its
275+ # attributes will avoid unwanted side-effects.
276+ if type_ != types .InstanceType :
277+ __getattr__ = getattr (type_ , '__getattr__' , None )
278+ if __getattr__ is not None :
279+ try :
280+ setattr (type_ , '__getattr__' , (lambda _ : None ))
281+ except TypeError :
282+ __getattr__ = None
283+ __getattribute__ = getattr (type_ , '__getattribute__' , None )
284+ if __getattribute__ is not None :
285+ try :
286+ setattr (type_ , '__getattribute__' , object .__getattribute__ )
287+ except TypeError :
288+ # XXX: This happens for e.g. built-in types
289+ __getattribute__ = None
290+ self .attribs = (__getattribute__ , __getattr__ )
291+ # /Dark magic
292+
293+ def __exit__ (self , exc_type , exc_val , exc_tb ):
294+ """Restore an object's magic methods."""
295+ type_ = type (self .obj )
296+ __getattribute__ , __getattr__ = self .attribs
297+ # Dark magic:
298+ if __getattribute__ is not None :
299+ setattr (type_ , '__getattribute__' , __getattribute__ )
300+ if __getattr__ is not None :
301+ setattr (type_ , '__getattr__' , __getattr__ )
302+ # /Dark magic
255303
256304class Repl (object ):
257305 """Implements the necessary guff for a Python-repl-alike interface
@@ -344,50 +392,6 @@ def __init__(self, scr, interp, statusbar=None, idle=None):
344392 'ignore' ) as hfile :
345393 self .rl_hist = hfile .readlines ()
346394
347- def clean_object (self , obj ):
348- """Try to make an object not exhibit side-effects on attribute
349- lookup. Return the type's magic attributes so they can be reapplied
350- with restore_object"""
351- type_ = type (obj )
352- __getattribute__ = None
353- __getattr__ = None
354- # Dark magic:
355- # If __getattribute__ doesn't exist on the class and __getattr__ does
356- # then __getattr__ will be called when doing
357- # getattr(type_, '__getattribute__', None)
358- # so we need to first remove the __getattr__, then the
359- # __getattribute__, then look up the attributes and then restore the
360- # original methods. :-(
361- # The upshot being that introspecting on an object to display its
362- # attributes will avoid unwanted side-effects.
363- if type_ != types .InstanceType :
364- __getattr__ = getattr (type_ , '__getattr__' , None )
365- if __getattr__ is not None :
366- try :
367- setattr (type_ , '__getattr__' , (lambda _ : None ))
368- except TypeError :
369- __getattr__ = None
370- __getattribute__ = getattr (type_ , '__getattribute__' , None )
371- if __getattribute__ is not None :
372- try :
373- setattr (type_ , '__getattribute__' , object .__getattribute__ )
374- except TypeError :
375- # XXX: This happens for e.g. built-in types
376- __getattribute__ = None
377- # /Dark magic
378- return __getattribute__ , __getattr__
379-
380- def restore_object (self , obj , attribs ):
381- """Restore an object's magic methods as returned from clean_object"""
382- type_ = type (obj )
383- __getattribute__ , __getattr__ = attribs
384- # Dark magic:
385- if __getattribute__ is not None :
386- setattr (type_ , '__getattribute__' , __getattribute__ )
387- if __getattr__ is not None :
388- setattr (type_ , '__getattr__' , __getattr__ )
389- # /Dark magic
390-
391395 def attr_matches (self , text ):
392396 """Taken from rlcompleter.py and bent to my will."""
393397
@@ -397,11 +401,8 @@ def attr_matches(self, text):
397401
398402 expr , attr = m .group (1 , 3 )
399403 obj = eval (expr , self .interp .locals )
400- attribs = self .clean_object (obj )
401- try :
404+ with AttrCleaner (obj ):
402405 matches = self .attr_lookup (obj , expr , attr )
403- finally :
404- self .restore_object (obj , attribs )
405406 return matches
406407
407408 def attr_lookup (self , obj , expr , attr ):
@@ -422,12 +423,9 @@ def attr_lookup(self, obj, expr, attr):
422423
423424 def _callable_postfix (self , value , word ):
424425 """rlcompleter's _callable_postfix done right."""
425- attribs = self .clean_object (value )
426- try :
426+ with AttrCleaner (value ):
427427 if hasattr (value , '__call__' ):
428428 word += '('
429- finally :
430- self .restore_object (value , attribs )
431429 return word
432430
433431 def cw (self ):
@@ -469,7 +467,7 @@ def getpydocspec(f, func):
469467 if s is None :
470468 return None
471469
472- if s .groups ()[0 ] != func :
470+ if not hasattr ( f , '__name__' ) or s .groups ()[0 ] != f . __name__ :
473471 return None
474472
475473 args = [i .strip () for i in s .groups ()[1 ].split (',' )]
@@ -500,7 +498,8 @@ def getargspec(func):
500498 return True
501499
502500 except (NameError , TypeError , KeyError ):
503- t = getpydocspec (f , func )
501+ with AttrCleaner (f ):
502+ t = getpydocspec (f , func )
504503 if t is None :
505504 return None
506505 self .argspec = t
0 commit comments