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