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 |
|
|
|
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