]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/PyCrust/introspect.py
d568dc6c6fe3ff1a901a092acac1289d65626106
1 """Provides a variety of introspective-type support functions for things
2 like call tips and command auto completion."""
4 __author__
= "Patrick K. O'Brien <pobrien@orbtech.com>"
6 __revision__
= "$Revision$"[11:-2]
12 def getAutoCompleteList(command
='', locals=None, includeMagic
=1, \
13 includeSingle
=1, includeDouble
=1):
14 """Return list of auto-completion options for command.
16 The list of options will be based on the locals namespace."""
18 # Get the proper chunk of code from the command.
19 root
= getRoot(command
, terminator
='.')
21 if locals is not None:
22 object = eval(root
, locals)
28 attributes
= getAttributeNames(object, includeMagic
, \
29 includeSingle
, includeDouble
)
32 def getAttributeNames(object, includeMagic
=1, includeSingle
=1, includeDouble
=1):
33 """Return list of unique attributes, including inherited, for an object."""
36 if not hasattrAlwaysReturnsTrue(object):
37 # Add some attributes that don't always get picked up.
38 # If they don't apply, they'll get filtered out at the end.
39 attributes
+= ['__bases__', '__class__', '__dict__', '__name__', \
40 'func_closure', 'func_code', 'func_defaults', \
41 'func_dict', 'func_doc', 'func_globals', 'func_name']
43 try: attributes
+= object._getAttributeNames
()
45 # Get all attribute names.
46 attrdict
= getAllAttributeNames(object)
47 for attrlist
in attrdict
.values():
48 attributes
+= attrlist
49 # Remove duplicates from the attribute list.
50 for item
in attributes
:
52 attributes
= dict.keys()
53 attributes
.sort(lambda x
, y
: cmp(x
.upper(), y
.upper()))
55 attributes
= filter(lambda item
: item
[0]!='_' \
56 or item
[1]=='_', attributes
)
58 attributes
= filter(lambda item
: item
[:2]!='__', attributes
)
59 # Make sure we haven't picked up any bogus attributes somehow.
60 attributes
= [attribute
for attribute
in attributes \
61 if hasattr(object, attribute
)]
64 def hasattrAlwaysReturnsTrue(object):
65 return hasattr(object, 'bogu5_123_aTTri8ute')
67 def getAllAttributeNames(object):
68 """Return mapping of all attributes, including inherited, for an object.
70 Recursively walk through a class and all base classes.
72 attrdict
= {} # (object, technique, count): [list of attributes]
74 # !!! Do Not use hasattr() as a test anywhere in this function,
75 # !!! because it is unreliable with remote objects - xmlrpc, soap, etc.
76 # !!! They always return true for hasattr().
79 # Yes, this can fail if object is an instance of a class with
80 # __str__ (or __repr__) having a bug or raising an exception. :-(
84 # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
85 wakeupcall
= dir(object)
87 # Get attributes available through the normal convention.
88 attributes
= dir(object)
89 attrdict
[(key
, 'dir', len(attributes
))] = attributes
90 # Get attributes from the object's dictionary, if it has one.
92 attributes
= object.__dict
__.keys()
94 except: # Must catch all because object might have __getattr__.
97 attrdict
[(key
, '__dict__', len(attributes
))] = attributes
98 # For a class instance, get the attributes for the class.
100 klass
= object.__class
__
101 except: # Must catch all because object might have __getattr__.
105 # Break a circular reference. This happens with extension classes.
108 attrdict
.update(getAllAttributeNames(klass
))
109 # Also get attributes from any and all parent classes.
111 bases
= object.__bases
__
112 except: # Must catch all because object might have __getattr__.
115 if isinstance(bases
, types
.TupleType
):
117 if type(base
) is types
.TypeType
:
118 # Break a circular reference. Happens in Python 2.2.
121 attrdict
.update(getAllAttributeNames(base
))
124 def getCallTip(command
='', locals=None):
125 """For a command, return a tuple of object name, argspec, tip text.
127 The call tip information will be based on the locals namespace."""
128 calltip
= ('', '', '') # object name, argspec, tip text.
129 # Get the proper chunk of code from the command.
130 root
= getRoot(command
, terminator
='(')
132 if locals is not None:
133 object = eval(root
, locals)
139 object, dropSelf
= getBaseObject(object)
141 name
= object.__name
__
142 except AttributeError:
146 if inspect
.isbuiltin(object):
147 # Builtin functions don't have an argspec that we can get.
149 elif inspect
.isfunction(object):
150 # tip1 is a string like: "getCallTip(command='', locals=None)"
151 argspec
= apply(inspect
.formatargspec
, inspect
.getargspec(object))
153 # The first parameter to a method is a reference to an
154 # instance, usually coded as "self", and is usually passed
155 # automatically by Python and therefore we want to drop it.
156 temp
= argspec
.split(',')
157 if len(temp
) == 1: # No other arguments.
159 else: # Drop the first argument.
160 argspec
= '(' + ','.join(temp
[1:]).lstrip()
161 tip1
= name
+ argspec
164 doc
= inspect
.getdoc(object)
166 # tip2 is the first separated line of the docstring, like:
167 # "Return call tip text for a command."
168 # tip3 is the rest of the docstring, like:
169 # "The call tip information will be based on ... <snip>
170 docpieces
= doc
.split('\n\n')
172 tip3
= '\n\n'.join(docpieces
[1:])
173 tip
= '%s\n\n%s\n\n%s' % (tip1
, tip2
, tip3
)
176 calltip
= (name
, argspec
[1:-1], tip
.strip())
179 def getRoot(command
, terminator
=None):
180 """Return the rightmost root portion of an arbitrary Python command.
182 Return only the root portion that can be eval()'d without side effects.
183 The command would normally terminate with a "(" or ".". The terminator
184 and anything after the terminator will be dropped."""
186 validChars
= "._" + string
.uppercase
+ string
.lowercase
+ string
.digits
187 emptyTypes
= ("''", '""', '""""""', "''''''", '[]', '()', '{}')
188 validSeparators
= string
.whitespace
+ ',+-*/=%<>&|^~:([{'
189 # Drop the final terminator and anything that follows.
190 command
= rtrimTerminus(command
, terminator
)
191 if len(command
) == 0:
193 elif command
in emptyTypes
and terminator
in ('.', '', None):
194 # Let empty type delimiter pairs go through.
197 # Go backward through the command until we hit an "invalid" character.
199 while i
and command
[i
-1] in validChars
:
201 # Default to everything from the "invalid" character to the end.
203 # Override certain exceptions.
204 if i
> 0 and command
[i
-1] in ("'", '"'):
205 # Detect situations where we are in the middle of a string.
206 # This code catches the simplest case, but needs to catch others.
208 elif ((2 <= i
< len(command
) and command
[i
] == '.') \
209 or (2 <= i
<= len(command
) and terminator
in ('.', '', None))) \
210 and command
[i
-2:i
] in emptyTypes
:
211 # Allow empty types to get through.
212 # Don't confuse an empty tupple with an argumentless callable.
213 if i
== 2 or (i
>= 3 and command
[i
-3] in validSeparators
):
217 def rtrimTerminus(command
, terminator
=None):
218 """Return command minus the final terminator and anything that follows."""
220 pieces
= command
.split(terminator
)
222 command
= terminator
.join(pieces
[:-1])
225 def getBaseObject(object):
226 """Return base object and dropSelf indicator for an object."""
227 if inspect
.isbuiltin(object):
228 # Builtin functions don't have an argspec that we can get.
230 elif inspect
.ismethod(object):
231 # Get the function from the object otherwise inspect.getargspec()
232 # complains that the object isn't a Python function.
234 if object.im_self
is None:
235 # This is an unbound method so we do not drop self from the
236 # argspec, since an instance must be passed as the first arg.
240 object = object.im_func
241 except AttributeError:
243 elif inspect
.isclass(object):
244 # Get the __init__ method function for the class.
245 constructor
= getConstructor(object)
246 if constructor
is not None:
251 elif callable(object):
252 # Get the __call__ method instead.
254 object = object.__call
__.im_func
256 except AttributeError:
260 return object, dropSelf
262 def getConstructor(object):
263 """Return constructor for class object, or None if there isn't one."""
265 return object.__init
__.im_func
266 except AttributeError:
267 for base
in object.__bases
__:
268 constructor
= getConstructor(base
)
269 if constructor
is not None: