PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/site-packages/wx-2.8-msw-unicode/wx/py/introspect.py

https://github.com/astory/RankPanda
Python | 380 lines | 340 code | 7 blank | 33 comment | 39 complexity | 16276424307c3617040120bd208abdcf MD5 | raw file
  1. """Provides a variety of introspective-type support functions for
  2. things like call tips and command auto completion."""
  3. __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
  4. __cvsid__ = "$Id: introspect.py 39896 2006-06-29 22:24:00Z RD $"
  5. __revision__ = "$Revision: 39896 $"[11:-2]
  6. import cStringIO
  7. import inspect
  8. import sys
  9. import tokenize
  10. import types
  11. import wx
  12. def getAutoCompleteList(command='', locals=None, includeMagic=1,
  13. includeSingle=1, includeDouble=1):
  14. """Return list of auto-completion options for command.
  15. The list of options will be based on the locals namespace."""
  16. attributes = []
  17. # Get the proper chunk of code from the command.
  18. root = getRoot(command, terminator='.')
  19. try:
  20. if locals is not None:
  21. object = eval(root, locals)
  22. else:
  23. object = eval(root)
  24. except:
  25. pass
  26. else:
  27. attributes = getAttributeNames(object, includeMagic,
  28. includeSingle, includeDouble)
  29. return attributes
  30. def getAttributeNames(object, includeMagic=1, includeSingle=1,
  31. includeDouble=1):
  32. """Return list of unique attributes, including inherited, for object."""
  33. attributes = []
  34. dict = {}
  35. if not hasattrAlwaysReturnsTrue(object):
  36. # Add some attributes that don't always get picked up.
  37. special_attrs = ['__bases__', '__class__', '__dict__', '__name__',
  38. 'func_closure', 'func_code', 'func_defaults',
  39. 'func_dict', 'func_doc', 'func_globals', 'func_name']
  40. attributes += [attr for attr in special_attrs \
  41. if hasattr(object, attr)]
  42. if includeMagic:
  43. try: attributes += object._getAttributeNames()
  44. except: pass
  45. # Get all attribute names.
  46. str_type = str(type(object))
  47. if str_type == "<type 'array'>":
  48. attributes += dir(object)
  49. else:
  50. attrdict = getAllAttributeNames(object)
  51. # Store the object's dir.
  52. object_dir = dir(object)
  53. for (obj_type_name, technique, count), attrlist in attrdict.items():
  54. # This complexity is necessary to avoid accessing all the
  55. # attributes of the object. This is very handy for objects
  56. # whose attributes are lazily evaluated.
  57. if type(object).__name__ == obj_type_name and technique == 'dir':
  58. attributes += attrlist
  59. else:
  60. attributes += [attr for attr in attrlist \
  61. if attr not in object_dir and hasattr(object, attr)]
  62. # Remove duplicates from the attribute list.
  63. for item in attributes:
  64. dict[item] = None
  65. attributes = dict.keys()
  66. # new-style swig wrappings can result in non-string attributes
  67. # e.g. ITK http://www.itk.org/
  68. attributes = [attribute for attribute in attributes \
  69. if type(attribute) == str]
  70. attributes.sort(lambda x, y: cmp(x.upper(), y.upper()))
  71. if not includeSingle:
  72. attributes = filter(lambda item: item[0]!='_' \
  73. or item[1:2]=='_', attributes)
  74. if not includeDouble:
  75. attributes = filter(lambda item: item[:2]!='__', attributes)
  76. return attributes
  77. def hasattrAlwaysReturnsTrue(object):
  78. return hasattr(object, 'bogu5_123_aTTri8ute')
  79. def getAllAttributeNames(object):
  80. """Return dict of all attributes, including inherited, for an object.
  81. Recursively walk through a class and all base classes.
  82. """
  83. attrdict = {} # (object, technique, count): [list of attributes]
  84. # !!!
  85. # Do Not use hasattr() as a test anywhere in this function,
  86. # because it is unreliable with remote objects: xmlrpc, soap, etc.
  87. # They always return true for hasattr().
  88. # !!!
  89. try:
  90. # This could(?) fail if the type is poorly defined without
  91. # even a name.
  92. key = type(object).__name__
  93. except:
  94. key = 'anonymous'
  95. # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
  96. wakeupcall = dir(object)
  97. del wakeupcall
  98. # Get attributes available through the normal convention.
  99. attributes = dir(object)
  100. attrdict[(key, 'dir', len(attributes))] = attributes
  101. # Get attributes from the object's dictionary, if it has one.
  102. try:
  103. attributes = object.__dict__.keys()
  104. attributes.sort()
  105. except: # Must catch all because object might have __getattr__.
  106. pass
  107. else:
  108. attrdict[(key, '__dict__', len(attributes))] = attributes
  109. # For a class instance, get the attributes for the class.
  110. try:
  111. klass = object.__class__
  112. except: # Must catch all because object might have __getattr__.
  113. pass
  114. else:
  115. if klass is object:
  116. # Break a circular reference. This happens with extension
  117. # classes.
  118. pass
  119. else:
  120. attrdict.update(getAllAttributeNames(klass))
  121. # Also get attributes from any and all parent classes.
  122. try:
  123. bases = object.__bases__
  124. except: # Must catch all because object might have __getattr__.
  125. pass
  126. else:
  127. if isinstance(bases, types.TupleType):
  128. for base in bases:
  129. if type(base) is types.TypeType:
  130. # Break a circular reference. Happens in Python 2.2.
  131. pass
  132. else:
  133. attrdict.update(getAllAttributeNames(base))
  134. return attrdict
  135. def getCallTip(command='', locals=None):
  136. """For a command, return a tuple of object name, argspec, tip text.
  137. The call tip information will be based on the locals namespace."""
  138. calltip = ('', '', '') # object name, argspec, tip text.
  139. # Get the proper chunk of code from the command.
  140. root = getRoot(command, terminator='(')
  141. try:
  142. if locals is not None:
  143. object = eval(root, locals)
  144. else:
  145. object = eval(root)
  146. except:
  147. return calltip
  148. name = ''
  149. object, dropSelf = getBaseObject(object)
  150. try:
  151. name = object.__name__
  152. except AttributeError:
  153. pass
  154. tip1 = ''
  155. argspec = ''
  156. if inspect.isbuiltin(object):
  157. # Builtin functions don't have an argspec that we can get.
  158. pass
  159. elif inspect.isfunction(object):
  160. # tip1 is a string like: "getCallTip(command='', locals=None)"
  161. argspec = apply(inspect.formatargspec, inspect.getargspec(object))
  162. if dropSelf:
  163. # The first parameter to a method is a reference to an
  164. # instance, usually coded as "self", and is usually passed
  165. # automatically by Python; therefore we want to drop it.
  166. temp = argspec.split(',')
  167. if len(temp) == 1: # No other arguments.
  168. argspec = '()'
  169. elif temp[0][:2] == '(*': # first param is like *args, not self
  170. pass
  171. else: # Drop the first argument.
  172. argspec = '(' + ','.join(temp[1:]).lstrip()
  173. tip1 = name + argspec
  174. doc = ''
  175. if callable(object):
  176. try:
  177. doc = inspect.getdoc(object)
  178. except:
  179. pass
  180. if doc:
  181. # tip2 is the first separated line of the docstring, like:
  182. # "Return call tip text for a command."
  183. # tip3 is the rest of the docstring, like:
  184. # "The call tip information will be based on ... <snip>
  185. firstline = doc.split('\n')[0].lstrip()
  186. if tip1 == firstline or firstline[:len(name)+1] == name+'(':
  187. tip1 = ''
  188. else:
  189. tip1 += '\n\n'
  190. docpieces = doc.split('\n\n')
  191. tip2 = docpieces[0]
  192. tip3 = '\n\n'.join(docpieces[1:])
  193. tip = '%s%s\n\n%s' % (tip1, tip2, tip3)
  194. else:
  195. tip = tip1
  196. calltip = (name, argspec[1:-1], tip.strip())
  197. return calltip
  198. def getRoot(command, terminator=None):
  199. """Return the rightmost root portion of an arbitrary Python command.
  200. Return only the root portion that can be eval()'d without side
  201. effects. The command would normally terminate with a '(' or
  202. '.'. The terminator and anything after the terminator will be
  203. dropped."""
  204. command = command.split('\n')[-1]
  205. if command.startswith(sys.ps2):
  206. command = command[len(sys.ps2):]
  207. command = command.lstrip()
  208. command = rtrimTerminus(command, terminator)
  209. tokens = getTokens(command)
  210. if not tokens:
  211. return ''
  212. if tokens[-1][0] is tokenize.ENDMARKER:
  213. # Remove the end marker.
  214. del tokens[-1]
  215. if not tokens:
  216. return ''
  217. if terminator == '.' and \
  218. (tokens[-1][1] <> '.' or tokens[-1][0] is not tokenize.OP):
  219. # Trap decimals in numbers, versus the dot operator.
  220. return ''
  221. else:
  222. # Strip off the terminator.
  223. if terminator and command.endswith(terminator):
  224. size = 0 - len(terminator)
  225. command = command[:size]
  226. command = command.rstrip()
  227. tokens = getTokens(command)
  228. tokens.reverse()
  229. line = ''
  230. start = None
  231. prefix = ''
  232. laststring = '.'
  233. emptyTypes = ('[]', '()', '{}')
  234. for token in tokens:
  235. tokentype = token[0]
  236. tokenstring = token[1]
  237. line = token[4]
  238. if tokentype is tokenize.ENDMARKER:
  239. continue
  240. if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
  241. and laststring != '.':
  242. # We've reached something that's not part of the root.
  243. if prefix and line[token[3][1]] != ' ':
  244. # If it doesn't have a space after it, remove the prefix.
  245. prefix = ''
  246. break
  247. if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
  248. or (tokentype is tokenize.OP and tokenstring == '.'):
  249. if prefix:
  250. # The prefix isn't valid because it comes after a dot.
  251. prefix = ''
  252. break
  253. else:
  254. # start represents the last known good point in the line.
  255. start = token[2][1]
  256. elif len(tokenstring) == 1 and tokenstring in ('[({])}'):
  257. # Remember, we're working backwords.
  258. # So prefix += tokenstring would be wrong.
  259. if prefix in emptyTypes and tokenstring in ('[({'):
  260. # We've already got an empty type identified so now we
  261. # are in a nested situation and we can break out with
  262. # what we've got.
  263. break
  264. else:
  265. prefix = tokenstring + prefix
  266. else:
  267. # We've reached something that's not part of the root.
  268. break
  269. laststring = tokenstring
  270. if start is None:
  271. start = len(line)
  272. root = line[start:]
  273. if prefix in emptyTypes:
  274. # Empty types are safe to be eval()'d and introspected.
  275. root = prefix + root
  276. return root
  277. def getTokens(command):
  278. """Return list of token tuples for command."""
  279. # In case the command is unicode try encoding it
  280. if type(command) == unicode:
  281. try:
  282. command = command.encode(wx.GetDefaultPyEncoding())
  283. except UnicodeEncodeError:
  284. pass # otherwise leave it alone
  285. f = cStringIO.StringIO(command)
  286. # tokens is a list of token tuples, each looking like:
  287. # (type, string, (srow, scol), (erow, ecol), line)
  288. tokens = []
  289. # Can't use list comprehension:
  290. # tokens = [token for token in tokenize.generate_tokens(f.readline)]
  291. # because of need to append as much as possible before TokenError.
  292. try:
  293. ## This code wasn't backward compatible with Python 2.1.3.
  294. ##
  295. ## for token in tokenize.generate_tokens(f.readline):
  296. ## tokens.append(token)
  297. # This works with Python 2.1.3 (with nested_scopes).
  298. def eater(*args):
  299. tokens.append(args)
  300. tokenize.tokenize_loop(f.readline, eater)
  301. except tokenize.TokenError:
  302. # This is due to a premature EOF, which we expect since we are
  303. # feeding in fragments of Python code.
  304. pass
  305. return tokens
  306. def rtrimTerminus(command, terminator=None):
  307. """Return command minus anything that follows the final terminator."""
  308. if terminator:
  309. pieces = command.split(terminator)
  310. if len(pieces) > 1:
  311. command = terminator.join(pieces[:-1]) + terminator
  312. return command
  313. def getBaseObject(object):
  314. """Return base object and dropSelf indicator for an object."""
  315. if inspect.isbuiltin(object):
  316. # Builtin functions don't have an argspec that we can get.
  317. dropSelf = 0
  318. elif inspect.ismethod(object):
  319. # Get the function from the object otherwise
  320. # inspect.getargspec() complains that the object isn't a
  321. # Python function.
  322. try:
  323. if object.im_self is None:
  324. # This is an unbound method so we do not drop self
  325. # from the argspec, since an instance must be passed
  326. # as the first arg.
  327. dropSelf = 0
  328. else:
  329. dropSelf = 1
  330. object = object.im_func
  331. except AttributeError:
  332. dropSelf = 0
  333. elif inspect.isclass(object):
  334. # Get the __init__ method function for the class.
  335. constructor = getConstructor(object)
  336. if constructor is not None:
  337. object = constructor
  338. dropSelf = 1
  339. else:
  340. dropSelf = 0
  341. elif callable(object):
  342. # Get the __call__ method instead.
  343. try:
  344. object = object.__call__.im_func
  345. dropSelf = 1
  346. except AttributeError:
  347. dropSelf = 0
  348. else:
  349. dropSelf = 0
  350. return object, dropSelf
  351. def getConstructor(object):
  352. """Return constructor for class object, or None if there isn't one."""
  353. try:
  354. return object.__init__.im_func
  355. except AttributeError:
  356. for base in object.__bases__:
  357. constructor = getConstructor(base)
  358. if constructor is not None:
  359. return constructor
  360. return None