##// END OF EJS Templates
Merge pull request #4382 from jtratner/safer-hasattr-in-dir2...
Thomas Kluyver -
r12970:2cdbe39e merge
parent child Browse files
Show More
@@ -36,6 +36,7 b' from IPython.utils import PyColorize'
36 36 from IPython.utils import io
37 37 from IPython.utils import openpy
38 38 from IPython.utils import py3compat
39 from IPython.utils.dir2 import safe_hasattr
39 40 from IPython.utils.text import indent
40 41 from IPython.utils.wildcard import list_namespace
41 42 from IPython.utils.coloransi import *
@@ -258,16 +259,6 b' def call_tip(oinfo, format_call=True):'
258 259
259 260 return call_line, doc
260 261
261 def safe_hasattr(obj, attr):
262 """In recent versions of Python, hasattr() only catches AttributeError.
263 This catches all errors.
264 """
265 try:
266 getattr(obj, attr)
267 return True
268 except:
269 return False
270
271 262
272 263 def find_file(obj):
273 264 """Find the absolute path to the file where an object was defined.
@@ -12,14 +12,25 b''
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15
16 15 #-----------------------------------------------------------------------------
17 16 # Code
18 17 #-----------------------------------------------------------------------------
19 18
19
20 def safe_hasattr(obj, attr):
21 """In recent versions of Python, hasattr() only catches AttributeError.
22 This catches all errors.
23 """
24 try:
25 getattr(obj, attr)
26 return True
27 except:
28 return False
29
30
20 31 def get_class_members(cls):
21 32 ret = dir(cls)
22 if hasattr(cls, '__bases__'):
33 if safe_hasattr(cls, '__bases__'):
23 34 try:
24 35 bases = cls.__bases__
25 36 except AttributeError:
@@ -49,7 +60,7 b' def dir2(obj):'
49 60
50 61 words = set(dir(obj))
51 62
52 if hasattr(obj, '__class__'):
63 if safe_hasattr(obj, '__class__'):
53 64 #words.add('__class__')
54 65 words |= set(get_class_members(obj.__class__))
55 66
@@ -57,14 +68,13 b' def dir2(obj):'
57 68 # for objects with Enthought's traits, add trait_names() list
58 69 # for PyCrust-style, add _getAttributeNames() magic method list
59 70 for attr in ('trait_names', '_getAttributeNames'):
60 if hasattr(obj, attr):
61 try:
62 func = getattr(obj, attr)
63 if callable(func):
64 words |= set(func())
65 except:
66 # TypeError: obj is class not instance
67 pass
71 try:
72 func = getattr(obj, attr)
73 if callable(func):
74 words |= set(func())
75 except:
76 # TypeError: obj is class not instance
77 pass
68 78
69 79 # filter out non-string attributes which may be stuffed by dir() calls
70 80 # and poor coding in third-party modules
@@ -50,3 +50,31 b' def test_SubClass_with_trait_names_attr():'
50 50
51 51 res = dir2(SubClass())
52 52 assert('trait_names' in res)
53
54
55 def test_misbehaving_object_without_trait_names():
56 # dir2 shouldn't raise even when objects are dumb and raise
57 # something other than AttribteErrors on bad getattr.
58
59 class BadTraitNames(object):
60 @property
61 def trait_names(self):
62 raise KeyboardInterrupt("This should be caught")
63
64 def some_method(self):
65 pass
66
67 class MisbehavingGetattr(object):
68 def __getattr__(self):
69 raise KeyError("I should be caught")
70
71 def some_method(self):
72 pass
73
74 class SillierWithDir(MisbehavingGetattr):
75 def __dir__(self):
76 return ['some_method']
77
78 for bad_klass in (BadTraitNames, MisbehavingGetattr, SillierWithDir):
79 res = dir2(bad_klass())
80 assert('some_method' in res)
General Comments 0
You need to be logged in to leave comments. Login now