]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/py/introspect.py
1 """Provides a variety of introspective-type support functions for
2 things like call tips and command auto completion."""
4 __author__
= "Patrick K. O'Brien <pobrien@orbtech.com>"
6 __revision__
= "$Revision$"[11:-2]
15 def getAutoCompleteList(command
='', locals=None, includeMagic
=1,
16 includeSingle
=1, includeDouble
=1):
17 """Return list of auto-completion options for command.
19 The list of options will be based on the locals namespace."""
21 # Get the proper chunk of code from the command.
22 root
= getRoot(command
, terminator
='.')
24 if locals is not None:
25 object = eval(root
, locals)
31 attributes
= getAttributeNames(object, includeMagic
,
32 includeSingle
, includeDouble
)
35 def getAttributeNames(object, includeMagic
=1, includeSingle
=1,
37 """Return list of unique attributes, including inherited, for object."""
40 if not hasattrAlwaysReturnsTrue(object):
41 # Add some attributes that don't always get picked up.
42 special_attrs
= ['__bases__', '__class__', '__dict__', '__name__',
43 'func_closure', 'func_code', 'func_defaults',
44 'func_dict', 'func_doc', 'func_globals', 'func_name']
45 attributes
+= [attr
for attr
in special_attrs \
46 if hasattr(object, attr
)]
48 try: attributes
+= object._getAttributeNames
()
50 # Get all attribute names.
51 str_type
= str(type(object))
52 if str_type
== "<type 'array'>":
53 attributes
+= dir(object)
55 attrdict
= getAllAttributeNames(object)
56 # Store the object's dir.
57 object_dir
= dir(object)
58 for (obj_type_name
, technique
, count
), attrlist
in attrdict
.items():
59 # This complexity is necessary to avoid accessing all the
60 # attributes of the object. This is very handy for objects
61 # whose attributes are lazily evaluated.
62 if type(object).__name
__ == obj_type_name
and technique
== 'dir':
63 attributes
+= attrlist
65 attributes
+= [attr
for attr
in attrlist \
66 if attr
not in object_dir
and hasattr(object, attr
)]
68 # Remove duplicates from the attribute list.
69 for item
in attributes
:
71 attributes
= dict.keys()
72 # new-style swig wrappings can result in non-string attributes
73 # e.g. ITK http://www.itk.org/
74 attributes
= [attribute
for attribute
in attributes \
75 if type(attribute
) == str]
76 attributes
.sort(lambda x
, y
: cmp(x
.upper(), y
.upper()))
78 attributes
= filter(lambda item
: item
[0]!='_' \
79 or item
[1:2]=='_', attributes
)
81 attributes
= filter(lambda item
: item
[:2]!='__', attributes
)
84 def hasattrAlwaysReturnsTrue(object):
85 return hasattr(object, 'bogu5_123_aTTri8ute')
87 def getAllAttributeNames(object):
88 """Return dict of all attributes, including inherited, for an object.
90 Recursively walk through a class and all base classes.
92 attrdict
= {} # (object, technique, count): [list of attributes]
94 # Do Not use hasattr() as a test anywhere in this function,
95 # because it is unreliable with remote objects: xmlrpc, soap, etc.
96 # They always return true for hasattr().
99 # This could(?) fail if the type is poorly defined without
101 key
= type(object).__name
__
104 # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
105 wakeupcall
= dir(object)
107 # Get attributes available through the normal convention.
108 attributes
= dir(object)
109 attrdict
[(key
, 'dir', len(attributes
))] = attributes
110 # Get attributes from the object's dictionary, if it has one.
112 attributes
= object.__dict
__.keys()
114 except: # Must catch all because object might have __getattr__.
117 attrdict
[(key
, '__dict__', len(attributes
))] = attributes
118 # For a class instance, get the attributes for the class.
120 klass
= object.__class
__
121 except: # Must catch all because object might have __getattr__.
125 # Break a circular reference. This happens with extension
129 attrdict
.update(getAllAttributeNames(klass
))
130 # Also get attributes from any and all parent classes.
132 bases
= object.__bases
__
133 except: # Must catch all because object might have __getattr__.
136 if isinstance(bases
, types
.TupleType
):
138 if type(base
) is types
.TypeType
:
139 # Break a circular reference. Happens in Python 2.2.
142 attrdict
.update(getAllAttributeNames(base
))
145 def getCallTip(command
='', locals=None):
146 """For a command, return a tuple of object name, argspec, tip text.
148 The call tip information will be based on the locals namespace."""
149 calltip
= ('', '', '') # object name, argspec, tip text.
150 # Get the proper chunk of code from the command.
151 root
= getRoot(command
, terminator
='(')
153 if locals is not None:
154 object = eval(root
, locals)
160 object, dropSelf
= getBaseObject(object)
162 name
= object.__name
__
163 except AttributeError:
167 if inspect
.isbuiltin(object):
168 # Builtin functions don't have an argspec that we can get.
170 elif inspect
.isfunction(object):
171 # tip1 is a string like: "getCallTip(command='', locals=None)"
172 argspec
= apply(inspect
.formatargspec
, inspect
.getargspec(object))
174 # The first parameter to a method is a reference to an
175 # instance, usually coded as "self", and is usually passed
176 # automatically by Python; therefore we want to drop it.
177 temp
= argspec
.split(',')
178 if len(temp
) == 1: # No other arguments.
180 elif temp
[0][:2] == '(*': # first param is like *args, not self
182 else: # Drop the first argument.
183 argspec
= '(' + ','.join(temp
[1:]).lstrip()
184 tip1
= name
+ argspec
188 doc
= inspect
.getdoc(object)
192 # tip2 is the first separated line of the docstring, like:
193 # "Return call tip text for a command."
194 # tip3 is the rest of the docstring, like:
195 # "The call tip information will be based on ... <snip>
196 firstline
= doc
.split('\n')[0].lstrip()
197 if tip1
== firstline
or firstline
[:len(name
)+1] == name
+'(':
201 docpieces
= doc
.split('\n\n')
203 tip3
= '\n\n'.join(docpieces
[1:])
204 tip
= '%s%s\n\n%s' % (tip1
, tip2
, tip3
)
207 calltip
= (name
, argspec
[1:-1], tip
.strip())
210 def getRoot(command
, terminator
=None):
211 """Return the rightmost root portion of an arbitrary Python command.
213 Return only the root portion that can be eval()'d without side
214 effects. The command would normally terminate with a '(' or
215 '.'. The terminator and anything after the terminator will be
217 command
= command
.split('\n')[-1]
218 if command
.startswith(sys
.ps2
):
219 command
= command
[len(sys
.ps2
):]
220 command
= command
.lstrip()
221 command
= rtrimTerminus(command
, terminator
)
222 tokens
= getTokens(command
)
225 if tokens
[-1][0] is tokenize
.ENDMARKER
:
226 # Remove the end marker.
230 if terminator
== '.' and \
231 (tokens
[-1][1] <> '.' or tokens
[-1][0] is not tokenize
.OP
):
232 # Trap decimals in numbers, versus the dot operator.
235 # Strip off the terminator.
236 if terminator
and command
.endswith(terminator
):
237 size
= 0 - len(terminator
)
238 command
= command
[:size
]
239 command
= command
.rstrip()
240 tokens
= getTokens(command
)
246 emptyTypes
= ('[]', '()', '{}')
249 tokenstring
= token
[1]
251 if tokentype
is tokenize
.ENDMARKER
:
253 if tokentype
in (tokenize
.NAME
, tokenize
.STRING
, tokenize
.NUMBER
) \
254 and laststring
!= '.':
255 # We've reached something that's not part of the root.
256 if prefix
and line
[token
[3][1]] != ' ':
257 # If it doesn't have a space after it, remove the prefix.
260 if tokentype
in (tokenize
.NAME
, tokenize
.STRING
, tokenize
.NUMBER
) \
261 or (tokentype
is tokenize
.OP
and tokenstring
== '.'):
263 # The prefix isn't valid because it comes after a dot.
267 # start represents the last known good point in the line.
269 elif len(tokenstring
) == 1 and tokenstring
in ('[({])}'):
270 # Remember, we're working backwords.
271 # So prefix += tokenstring would be wrong.
272 if prefix
in emptyTypes
and tokenstring
in ('[({'):
273 # We've already got an empty type identified so now we
274 # are in a nested situation and we can break out with
278 prefix
= tokenstring
+ prefix
280 # We've reached something that's not part of the root.
282 laststring
= tokenstring
286 if prefix
in emptyTypes
:
287 # Empty types are safe to be eval()'d and introspected.
291 def getTokens(command
):
292 """Return list of token tuples for command."""
294 # In case the command is unicode try encoding it
295 if type(command
) == unicode:
297 command
= command
.encode(wx
.GetDefaultPyEncoding())
298 except UnicodeEncodeError:
299 pass # otherwise leave it alone
301 f
= cStringIO
.StringIO(command
)
302 # tokens is a list of token tuples, each looking like:
303 # (type, string, (srow, scol), (erow, ecol), line)
305 # Can't use list comprehension:
306 # tokens = [token for token in tokenize.generate_tokens(f.readline)]
307 # because of need to append as much as possible before TokenError.
309 ## This code wasn't backward compatible with Python 2.1.3.
311 ## for token in tokenize.generate_tokens(f.readline):
312 ## tokens.append(token)
314 # This works with Python 2.1.3 (with nested_scopes).
317 tokenize
.tokenize_loop(f
.readline
, eater
)
318 except tokenize
.TokenError
:
319 # This is due to a premature EOF, which we expect since we are
320 # feeding in fragments of Python code.
324 def rtrimTerminus(command
, terminator
=None):
325 """Return command minus anything that follows the final terminator."""
327 pieces
= command
.split(terminator
)
329 command
= terminator
.join(pieces
[:-1]) + terminator
332 def getBaseObject(object):
333 """Return base object and dropSelf indicator for an object."""
334 if inspect
.isbuiltin(object):
335 # Builtin functions don't have an argspec that we can get.
337 elif inspect
.ismethod(object):
338 # Get the function from the object otherwise
339 # inspect.getargspec() complains that the object isn't a
342 if object.im_self
is None:
343 # This is an unbound method so we do not drop self
344 # from the argspec, since an instance must be passed
349 object = object.im_func
350 except AttributeError:
352 elif inspect
.isclass(object):
353 # Get the __init__ method function for the class.
354 constructor
= getConstructor(object)
355 if constructor
is not None:
360 elif callable(object):
361 # Get the __call__ method instead.
363 object = object.__call
__.im_func
365 except AttributeError:
369 return object, dropSelf
371 def getConstructor(object):
372 """Return constructor for class object, or None if there isn't one."""
374 return object.__init
__.im_func
375 except AttributeError:
376 for base
in object.__bases
__:
377 constructor
= getConstructor(base
)
378 if constructor
is not None: