File: fix_introspect_bug.py

package info (click to toggle)
python-pyface 8.0.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,944 kB
  • sloc: python: 54,107; makefile: 82
file content (161 lines) | stat: -rw-r--r-- 5,723 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# (C) Copyright 2005-2023 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!

"""This module adds a fix for wx.py's introspect module.

In order to do code-completion, the function
`introspect.getAttributeName` accesses all the attributes of the
current object.  This causes severe problems for modules like tvtk
which depend on lazy importing.  The original introspect module also
has severe problems with large Numeric arrays because it calls str()
on the Numeric object in order to find its methods.

This file defines a fixed function that works fine with lazy objects
and large Numeric arrays.  This fixed function is injected into the
introspect module.
"""

# Import introspect.
from wx.py import introspect


# The fixed function.
def getAttributeNames(
    object, includeMagic=1, includeSingle=1, includeDouble=1
):
    """Return list of unique attributes, including inherited, for object."""
    attributes = []
    dict = {}
    if not introspect.hasattrAlwaysReturnsTrue(object):
        # Add some attributes that don't always get picked up.
        special_attrs = [
            "__bases__",
            "__class__",
            "__dict__",
            "__name__",
            "__closure__",
            "__code__",
            "___kwdefaults__",
            "__doc__",
            "__globals__",
        ]
        attributes += [attr for attr in special_attrs if hasattr(object, attr)]
    # For objects that have traits, get all the trait names since
    # these do not show up in dir(object).
    if hasattr(object, "trait_names"):
        try:
            attributes += object.trait_names()
        except TypeError:
            pass

    if includeMagic:
        try:
            attributes += object._getAttributeNames()
        except:
            pass
    # Get all attribute names.
    attrdict = getAllAttributeNames(object)
    # Store the object's dir.
    object_dir = dir(object)
    for (obj_type_name, technique, count), attrlist in attrdict.items():
        # This complexity is necessary to avoid accessing all the
        # attributes of the object.  This is very handy for objects
        # whose attributes are lazily evaluated.
        if type(object).__name__ == obj_type_name and technique == "dir":
            attributes += attrlist
        else:
            attributes += [
                attr
                for attr in attrlist
                if attr not in object_dir and hasattr(object, attr)
            ]

    # Remove duplicates from the attribute list.
    for item in attributes:
        dict[item] = None
    attributes = list(dict.keys())
    # new-style swig wrappings can result in non-string attributes
    # e.g. ITK http://www.itk.org/
    attributes = [
        attribute for attribute in attributes if isinstance(attribute, str)
    ]
    attributes.sort(key=lambda x: x.upper())
    if not includeSingle:
        attributes = filter(
            lambda item: item[0] != "_" or item[1] == "_", attributes
        )
    if not includeDouble:
        attributes = filter(lambda item: item[:2] != "__", attributes)
    return attributes


# Replace introspect's version with ours.
introspect.getAttributeNames = getAttributeNames


# This is also a modified version of the function which does not use
# str(object).
def getAllAttributeNames(obj):
    """Return dict of all attributes, including inherited, for an object.

    Recursively walk through a class and all base classes.
    """
    attrdict = {}  # (object, technique, count): [list of attributes]
    # !!!
    # Do Not use hasattr() as a test anywhere in this function,
    # because it is unreliable with remote objects: xmlrpc, soap, etc.
    # They always return true for hasattr().
    # !!!
    try:
        # This could(?) fail if the type is poorly defined without
        # even a name.
        key = type(obj).__name__
    except:
        key = "anonymous"
    # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
    wakeupcall = dir(obj)
    del wakeupcall
    # Get attributes available through the normal convention.
    attributes = dir(obj)
    attrdict[(key, "dir", len(attributes))] = attributes
    # Get attributes from the object's dictionary, if it has one.
    try:
        attributes = list(obj.__dict__.keys())
        attributes.sort()
    except:  # Must catch all because object might have __getattr__.
        pass
    else:
        attrdict[(key, "__dict__", len(attributes))] = attributes
    # For a class instance, get the attributes for the class.
    try:
        klass = obj.__class__
    except:  # Must catch all because object might have __getattr__.
        pass
    else:
        if klass is obj:
            # Break a circular reference. This happens with extension
            # classes.
            pass
        else:
            attrdict.update(getAllAttributeNames(klass))
    # Also get attributes from any and all parent classes.
    try:
        bases = obj.__bases__
    except:  # Must catch all because object might have __getattr__.
        pass
    else:
        if isinstance(bases, tuple):
            for base in bases:
                if isinstance(base, (tuple, object)):
                    # Break a circular reference. Happens in Python 2.2. & 3.6 (prob others)
                    pass
                else:
                    attrdict.update(getAllAttributeNames(base))
    return attrdict