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
|