##// END OF EJS Templates
Use safe_hasattr in dir2...
Jeffrey Tratner -
Show More
@@ -36,6 +36,7 b' from IPython.utils import PyColorize'
36 from IPython.utils import io
36 from IPython.utils import io
37 from IPython.utils import openpy
37 from IPython.utils import openpy
38 from IPython.utils import py3compat
38 from IPython.utils import py3compat
39 from IPython.utils.dir2 import safe_hasattr
39 from IPython.utils.text import indent
40 from IPython.utils.text import indent
40 from IPython.utils.wildcard import list_namespace
41 from IPython.utils.wildcard import list_namespace
41 from IPython.utils.coloransi import *
42 from IPython.utils.coloransi import *
@@ -258,16 +259,6 b' def call_tip(oinfo, format_call=True):'
258
259
259 return call_line, doc
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 def find_file(obj):
263 def find_file(obj):
273 """Find the absolute path to the file where an object was defined.
264 """Find the absolute path to the file where an object was defined.
@@ -12,14 +12,25 b''
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
17 # Code
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 def get_class_members(cls):
31 def get_class_members(cls):
21 ret = dir(cls)
32 ret = dir(cls)
22 if hasattr(cls, '__bases__'):
33 if safe_hasattr(cls, '__bases__'):
23 try:
34 try:
24 bases = cls.__bases__
35 bases = cls.__bases__
25 except AttributeError:
36 except AttributeError:
@@ -49,7 +60,7 b' def dir2(obj):'
49
60
50 words = set(dir(obj))
61 words = set(dir(obj))
51
62
52 if hasattr(obj, '__class__'):
63 if safe_hasattr(obj, '__class__'):
53 #words.add('__class__')
64 #words.add('__class__')
54 words |= set(get_class_members(obj.__class__))
65 words |= set(get_class_members(obj.__class__))
55
66
@@ -57,14 +68,13 b' def dir2(obj):'
57 # for objects with Enthought's traits, add trait_names() list
68 # for objects with Enthought's traits, add trait_names() list
58 # for PyCrust-style, add _getAttributeNames() magic method list
69 # for PyCrust-style, add _getAttributeNames() magic method list
59 for attr in ('trait_names', '_getAttributeNames'):
70 for attr in ('trait_names', '_getAttributeNames'):
60 if hasattr(obj, attr):
71 try:
61 try:
72 func = getattr(obj, attr)
62 func = getattr(obj, attr)
73 if callable(func):
63 if callable(func):
74 words |= set(func())
64 words |= set(func())
75 except:
65 except:
76 # TypeError: obj is class not instance
66 # TypeError: obj is class not instance
77 pass
67 pass
68
78
69 # filter out non-string attributes which may be stuffed by dir() calls
79 # filter out non-string attributes which may be stuffed by dir() calls
70 # and poor coding in third-party modules
80 # and poor coding in third-party modules
@@ -50,3 +50,31 b' def test_SubClass_with_trait_names_attr():'
50
50
51 res = dir2(SubClass())
51 res = dir2(SubClass())
52 assert('trait_names' in res)
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