]> git.saurik.com Git - wxWidgets.git/blob - wxPython/wxPython/lib/PyCrust/introspect.py
8cf6ae85051e69d96b68bd9f9e90392786cb8579
[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 __date__ = "August 8, 2001"
7 __version__ = "$Revision$"[11:-2]
8
9 import inspect
10 import string
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)
23 if includeMagic:
24 try: attributes += object._getAttributeNames()
25 except: pass
26 if not includeSingle:
27 attributes = filter(lambda item: item[0]!='_' or item[1]=='_', attributes)
28 if not includeDouble:
29 attributes = filter(lambda item: item[:2]!='__', attributes)
30 return attributes
31 except:
32 return []
33
34 def getAttributeNames(object):
35 """Return list of unique attributes, including inherited, for an object."""
36 attributes = []
37 dict = {}
38 # Remove duplicates from the attribute list.
39 for item in getAllAttributeNames(object):
40 dict[item] = None
41 attributes += dict.keys()
42 attributes.sort()
43 return attributes
44
45 def getAllAttributeNames(object):
46 """Return list of all attributes, including inherited, for an object.
47
48 Recursively walk through a class and all base classes.
49 """
50 attributes = []
51 # Get attributes available through the normal convention.
52 attributes += dir(object)
53 # For a class instance, get the attributes for the class.
54 if hasattr(object, '__class__'):
55 attributes += getAllAttributeNames(object.__class__)
56 # Also get attributes from any and all parent classes.
57 if hasattr(object, '__bases__'):
58 for base in object.__bases__:
59 attributes += getAllAttributeNames(base)
60 return attributes
61
62 def getCallTip(command='', locals=None):
63 """Return call tip text for a command.
64
65 The call tip information will be based on the locals namespace."""
66
67 # Get the proper chunk of code from the command.
68 root = getRoot(command, terminator='(')
69 try:
70 object = eval(root, locals)
71 except:
72 return ''
73 dropSelf = 0
74 if hasattr(object, '__name__'): # Make sure this is a useable object.
75 # Switch to the object that has the information we need.
76 if inspect.ismethod(object):
77 # Get the function from the object otherwise inspec.getargspec()
78 # complains that the object isn't a Python function.
79 object = object.im_func
80 dropSelf = 1
81 elif inspect.isclass(object):
82 # Get the __init__ method for the class.
83 try:
84 object = object.__init__.im_func
85 dropSelf = 1
86 except AttributeError:
87 for base in object.__bases__:
88 constructor = _find_constructor(base)
89 if constructor is not None:
90 object = constructor
91 dropSelf = 1
92 break
93 name = object.__name__
94 if inspect.isbuiltin(object):
95 # Builtin functions don't have an argspec that we can get.
96 tip1 = ''
97 else:
98 # tip1 is a string like: "getCallTip(command='', locals=None)"
99 argspec = apply(inspect.formatargspec, inspect.getargspec(object))
100 if dropSelf:
101 # The first parameter to a method is a reference to the
102 # instance, usually coded as "self", and is passed
103 # automatically by Python and therefore we want to drop it.
104 temp = argspec.split(',')
105 if len(temp) == 1: # No other arguments.
106 argspec = '()'
107 else: # Drop the first argument.
108 argspec = '(' + ','.join(temp[1:]).lstrip()
109 tip1 = name + argspec
110 doc = inspect.getdoc(object)
111 if doc:
112 # tip2 is the first separated line of the docstring, like:
113 # "Return call tip text for a command."
114 # tip3 is the rest of the docstring, like:
115 # "The call tip information will be based on ... <snip>
116 docpieces = doc.split('\n\n')
117 tip2 = docpieces[0]
118 tip3 = '\n\n'.join(docpieces[1:])
119 tip = '%s\n\n%s\n\n%s' % (tip1, tip2, tip3)
120 else:
121 tip = tip1
122 return tip.strip()
123 else:
124 return ''
125
126 def getRoot(command, terminator=None):
127 """Return the rightmost root portion of an arbitrary Python command.
128
129 The command would normally terminate with a "(" or ".". Anything after
130 the terminator will be dropped, allowing you to get back to the root.
131 Return only the root portion that can be eval()'d without side effect.
132 """
133 root = ''
134 validChars = "._" + string.uppercase + string.lowercase + string.digits
135 if terminator:
136 # Drop the final terminator and anything that follows.
137 pieces = command.split(terminator)
138 if len(pieces) > 1:
139 command = terminator.join(pieces[:-1])
140 if command in ("''", '""', '""""""', '[]', '()', '{}'):
141 # Let empty type delimiter pairs go through.
142 root = command
143 else:
144 # Go backward through the command until we hit an "invalid" character.
145 i = len(command)
146 while i and command[i-1] in validChars:
147 i -= 1
148 # Detect situations where we are in the middle of a string.
149 # This code catches the simplest case, but needs to catch others.
150 if command[i-1] in ("'", '"'):
151 # Were in the middle of a string so we aren't dealing with an
152 # object and it would be misleading to return anything here.
153 root = ''
154 else:
155 # Grab everything from the "invalid" character to the end.
156 root = command[i:]
157 return root
158