]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/PyCrust/introspect.py
39561ade2c7f904683c38c0166a8d4d63833801c
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 # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
80 wakeupcall
= dir(object)
82 # Get attributes available through the normal convention.
83 attributes
= dir(object)
84 attrdict
[(key
, 'dir', len(attributes
))] = attributes
85 # Get attributes from the object's dictionary, if it has one.
87 attributes
= object.__dict
__.keys()
89 except: # Must catch all because object might have __getattr__.
92 attrdict
[(key
, '__dict__', len(attributes
))] = attributes
93 # For a class instance, get the attributes for the class.
95 klass
= object.__class
__
96 except: # Must catch all because object might have __getattr__.
100 # Break a circular reference. This happens with extension classes.
103 attrdict
.update(getAllAttributeNames(klass
))
104 # Also get attributes from any and all parent classes.
106 bases
= object.__bases
__
107 except: # Must catch all because object might have __getattr__.
110 if isinstance(bases
, types
.TupleType
):
112 if type(base
) is types
.TypeType
:
113 # Break a circular reference. Happens in Python 2.2.
116 attrdict
.update(getAllAttributeNames(base
))
119 def getCallTip(command
='', locals=None):
120 """For a command, return a tuple of object name, argspec, tip text.
122 The call tip information will be based on the locals namespace."""
123 calltip
= ('', '', '') # object name, argspec, tip text.
124 # Get the proper chunk of code from the command.
125 root
= getRoot(command
, terminator
='(')
127 if locals is not None:
128 object = eval(root
, locals)
134 object, dropSelf
= getBaseObject(object)
136 name
= object.__name
__
137 except AttributeError:
141 if inspect
.isbuiltin(object):
142 # Builtin functions don't have an argspec that we can get.
144 elif inspect
.isfunction(object):
145 # tip1 is a string like: "getCallTip(command='', locals=None)"
146 argspec
= apply(inspect
.formatargspec
, inspect
.getargspec(object))
148 # The first parameter to a method is a reference to an
149 # instance, usually coded as "self", and is usually passed
150 # automatically by Python and therefore we want to drop it.
151 temp
= argspec
.split(',')
152 if len(temp
) == 1: # No other arguments.
154 else: # Drop the first argument.
155 argspec
= '(' + ','.join(temp
[1:]).lstrip()
156 tip1
= name
+ argspec
157 doc
= inspect
.getdoc(object)
159 # tip2 is the first separated line of the docstring, like:
160 # "Return call tip text for a command."
161 # tip3 is the rest of the docstring, like:
162 # "The call tip information will be based on ... <snip>
163 docpieces
= doc
.split('\n\n')
165 tip3
= '\n\n'.join(docpieces
[1:])
166 tip
= '%s\n\n%s\n\n%s' % (tip1
, tip2
, tip3
)
169 calltip
= (name
, argspec
[1:-1], tip
.strip())
172 def getRoot(command
, terminator
=None):
173 """Return the rightmost root portion of an arbitrary Python command.
175 Return only the root portion that can be eval()'d without side effects.
176 The command would normally terminate with a "(" or ".". The terminator
177 and anything after the terminator will be dropped."""
179 validChars
= "._" + string
.uppercase
+ string
.lowercase
+ string
.digits
180 emptyTypes
= ("''", '""', '""""""', "''''''", '[]', '()', '{}')
181 validSeparators
= string
.whitespace
+ ',+-*/=%<>&|^~:([{'
182 # Drop the final terminator and anything that follows.
183 command
= rtrimTerminus(command
, terminator
)
184 if len(command
) == 0:
186 elif command
in emptyTypes
and terminator
in ('.', '', None):
187 # Let empty type delimiter pairs go through.
190 # Go backward through the command until we hit an "invalid" character.
192 while i
and command
[i
-1] in validChars
:
194 # Default to everything from the "invalid" character to the end.
196 # Override certain exceptions.
197 if i
> 0 and command
[i
-1] in ("'", '"'):
198 # Detect situations where we are in the middle of a string.
199 # This code catches the simplest case, but needs to catch others.
201 elif ((2 <= i
< len(command
) and command
[i
] == '.') \
202 or (2 <= i
<= len(command
) and terminator
in ('.', '', None))) \
203 and command
[i
-2:i
] in emptyTypes
:
204 # Allow empty types to get through.
205 # Don't confuse an empty tupple with an argumentless callable.
206 if i
== 2 or (i
>= 3 and command
[i
-3] in validSeparators
):
210 def rtrimTerminus(command
, terminator
=None):
211 """Return command minus the final terminator and anything that follows."""
213 pieces
= command
.split(terminator
)
215 command
= terminator
.join(pieces
[:-1])
218 def getBaseObject(object):
219 """Return base object and dropSelf indicator for an object."""
220 if inspect
.isbuiltin(object):
221 # Builtin functions don't have an argspec that we can get.
223 elif inspect
.ismethod(object):
224 # Get the function from the object otherwise inspect.getargspec()
225 # complains that the object isn't a Python function.
227 if object.im_self
is None:
228 # This is an unbound method so we do not drop self from the
229 # argspec, since an instance must be passed as the first arg.
233 object = object.im_func
234 except AttributeError:
236 elif inspect
.isclass(object):
237 # Get the __init__ method function for the class.
238 constructor
= getConstructor(object)
239 if constructor
is not None:
244 elif callable(object):
245 # Get the __call__ method instead.
247 object = object.__call
__.im_func
249 except AttributeError:
253 return object, dropSelf
255 def getConstructor(object):
256 """Return constructor for class object, or None if there isn't one."""
258 return object.__init
__.im_func
259 except AttributeError:
260 for base
in object.__bases
__:
261 constructor
= getConstructor(base
)
262 if constructor
is not None: