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