]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/PyCrust/introspect.py
7a09a40cd6906c60149a1df484547a1c43bbdf17
[wxWidgets.git] / wxPython / wxPython / lib / PyCrust / introspect.py
1 """Provides a variety of introspective-type support functions for things
2 like call tips and command auto completion."""
3
4 __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
5 __cvsid__ = "$Id$"
6 __version__ = "$Revision$"[11:-2]
7
8 import inspect
9 import string
10 import types
11
12 def getAutoCompleteList(command='', locals=None, includeMagic=1, \
13 includeSingle=1, includeDouble=1):
14 """Return list of auto-completion options for command.
15
16 The list of options will be based on the locals namespace."""
17
18 # Get the proper chunk of code from the command.
19 root = getRoot(command, terminator='.')
20 try:
21 object = eval(root, locals)
22 attributes = getAttributeNames(object, includeMagic, \
23 includeSingle, includeDouble)
24 return attributes
25 except:
26 return []
27
28 def getAttributeNames(object, includeMagic=1, includeSingle=1, includeDouble=1):
29 """Return list of unique attributes, including inherited, for an object."""
30 attributes = []
31 dict = {}
32 if includeMagic:
33 try: attributes += object._getAttributeNames()
34 except: pass
35 # Get all attribute names, removing duplicates from the attribute list.
36 for item in getAllAttributeNames(object):
37 dict[item] = None
38 attributes += dict.keys()
39 attributes.sort(lambda x, y: cmp(x.upper(), y.upper()))
40 if not includeSingle:
41 attributes = filter(lambda item: item[0]!='_' or item[1]=='_', attributes)
42 if not includeDouble:
43 attributes = filter(lambda item: item[:2]!='__', attributes)
44 # Make sure we haven't picked up any bogus attributes somehow.
45 attributes = [attribute for attribute in attributes if hasattr(object, attribute)]
46 return attributes
47
48 def getAllAttributeNames(object):
49 """Return list of all attributes, including inherited, for an object.
50
51 Recursively walk through a class and all base classes.
52 """
53 # !!!
54 # !!! Do Not use hasattr() as a test anywhere in this function,
55 # !!! because it is unreliable with remote objects - xmlrpc, soap, etc.
56 # !!!
57 attributes = []
58 # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
59 wakeupcall = dir(object)
60 del wakeupcall
61 # Get attributes available through the normal convention.
62 attributes += dir(object)
63 try:
64 keys = object.__dict__.keys()
65 except:
66 pass
67 else:
68 attributes += keys
69 # For a class instance, get the attributes for the class.
70 if hasattr(object, '__class__'):
71 # Break a circular reference. This happens with extension classes.
72 if object.__class__ is object:
73 pass
74 else:
75 attributes += getAllAttributeNames(object.__class__)
76 # Also get attributes from any and all parent classes.
77 try:
78 bases = object.__bases__
79 except:
80 pass
81 else:
82 if isinstance(bases, type(())):
83 for base in bases:
84 if type(base) is types.TypeType:
85 # Break a circular reference. Happens in Python 2.2.
86 pass
87 else:
88 attributes += getAllAttributeNames(base)
89 return attributes
90
91 def getCallTip(command='', locals=None):
92 """Return call tip text for a command.
93
94 The call tip information will be based on the locals namespace."""
95
96 # Get the proper chunk of code from the command.
97 root = getRoot(command, terminator='(')
98 try:
99 object = eval(root, locals)
100 except:
101 return ''
102 dropSelf = 0
103 if hasattr(object, '__name__'): # Make sure this is a useable object.
104 # Switch to the object that has the information we need.
105 if inspect.ismethod(object) or hasattr(object, 'im_func'):
106 # Get the function from the object otherwise inspect.getargspec()
107 # complains that the object isn't a Python function.
108 object = object.im_func
109 dropSelf = 1
110 elif inspect.isclass(object):
111 # Get the __init__ method function for the class.
112 constructor = getConstructor(object)
113 if constructor is not None:
114 object = constructor
115 dropSelf = 1
116 name = object.__name__
117 tip1 = ''
118 if inspect.isbuiltin(object):
119 # Builtin functions don't have an argspec that we can get.
120 pass
121 elif inspect.isfunction(object):
122 # tip1 is a string like: "getCallTip(command='', locals=None)"
123 argspec = apply(inspect.formatargspec, inspect.getargspec(object))
124 if dropSelf:
125 # The first parameter to a method is a reference to the
126 # instance, usually coded as "self", and is passed
127 # automatically by Python and therefore we want to drop it.
128 temp = argspec.split(',')
129 if len(temp) == 1: # No other arguments.
130 argspec = '()'
131 else: # Drop the first argument.
132 argspec = '(' + ','.join(temp[1:]).lstrip()
133 tip1 = name + argspec
134 doc = inspect.getdoc(object)
135 if doc:
136 # tip2 is the first separated line of the docstring, like:
137 # "Return call tip text for a command."
138 # tip3 is the rest of the docstring, like:
139 # "The call tip information will be based on ... <snip>
140 docpieces = doc.split('\n\n')
141 tip2 = docpieces[0]
142 tip3 = '\n\n'.join(docpieces[1:])
143 tip = '%s\n\n%s\n\n%s' % (tip1, tip2, tip3)
144 else:
145 tip = tip1
146 return tip.strip()
147 else:
148 return ''
149
150 def getConstructor(object):
151 """Return constructor for class object, or None if there isn't one."""
152 try:
153 return object.__init__.im_func
154 except AttributeError:
155 for base in object.__bases__:
156 constructor = getConstructor(base)
157 if constructor is not None:
158 return constructor
159 return None
160
161 def getRoot(command, terminator=None):
162 """Return the rightmost root portion of an arbitrary Python command.
163
164 The command would normally terminate with a "(" or ".". Anything after
165 the terminator will be dropped, allowing you to get back to the root.
166 Return only the root portion that can be eval()'d without side effects.
167 """
168 root = ''
169 validChars = "._" + string.uppercase + string.lowercase + string.digits
170 if terminator:
171 # Drop the final terminator and anything that follows.
172 pieces = command.split(terminator)
173 if len(pieces) > 1:
174 command = terminator.join(pieces[:-1])
175 if len(command) == 0:
176 root = ''
177 elif command in ("''", '""', '""""""', '[]', '()', '{}'):
178 # Let empty type delimiter pairs go through.
179 root = command
180 else:
181 # Go backward through the command until we hit an "invalid" character.
182 i = len(command)
183 while i and command[i-1] in validChars:
184 i -= 1
185 # Detect situations where we are in the middle of a string.
186 # This code catches the simplest case, but needs to catch others.
187 if command[i-1] in ("'", '"'):
188 # We're in the middle of a string so we aren't dealing with an
189 # object and it would be misleading to return anything here.
190 root = ''
191 else:
192 # Grab everything from the "invalid" character to the end.
193 root = command[i:]
194 return root
195
196
197