]>
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] 
   8 from __future__ 
import nested_scopes
 
  17 def getAutoCompleteList(command
='', locals=None, includeMagic
=1,  
  18                         includeSingle
=1, includeDouble
=1): 
  19     """Return list of auto-completion options for command. 
  21     The list of options will be based on the locals namespace.""" 
  23     # Get the proper chunk of code from the command. 
  24     root 
= getRoot(command
, terminator
='.') 
  26         if locals is not None: 
  27             object = eval(root
, locals) 
  33         attributes 
= getAttributeNames(object, includeMagic
,  
  34                                        includeSingle
, includeDouble
) 
  37 def getAttributeNames(object, includeMagic
=1, includeSingle
=1, 
  39     """Return list of unique attributes, including inherited, for object.""" 
  42     if not hasattrAlwaysReturnsTrue(object): 
  43         # Add some attributes that don't always get picked up. 
  44         special_attrs 
= ['__bases__', '__class__', '__dict__', '__name__', 
  45                          'func_closure', 'func_code', 'func_defaults', 
  46                          'func_dict', 'func_doc', 'func_globals', 'func_name'] 
  47         attributes 
+= [attr 
for attr 
in special_attrs \
 
  48                        if hasattr(object, attr
)] 
  50         try: attributes 
+= object._getAttributeNames
() 
  52     # Get all attribute names. 
  53     str_type 
= str(type(object)) 
  54     if str_type 
== "<type 'array'>": 
  55         attributes 
+= dir(object) 
  57         attrdict 
= getAllAttributeNames(object) 
  58         # Store the object's dir. 
  59         object_dir 
= dir(object) 
  60         for (obj_type_name
, technique
, count
), attrlist 
in attrdict
.items(): 
  61             # This complexity is necessary to avoid accessing all the 
  62             # attributes of the object.  This is very handy for objects 
  63             # whose attributes are lazily evaluated. 
  64             if type(object).__name
__ == obj_type_name 
and technique 
== 'dir': 
  65                 attributes 
+= attrlist
 
  67                 attributes 
+= [attr 
for attr 
in attrlist \
 
  68                                if attr 
not in object_dir 
and hasattr(object, attr
)] 
  70     # Remove duplicates from the attribute list. 
  71     for item 
in attributes
: 
  73     attributes 
= dict.keys() 
  74     # new-style swig wrappings can result in non-string attributes 
  75     # e.g. ITK http://www.itk.org/ 
  76     attributes 
= [attribute 
for attribute 
in attributes \
 
  77                   if type(attribute
) == str] 
  78     attributes
.sort(lambda x
, y
: cmp(x
.upper(), y
.upper())) 
  80         attributes 
= filter(lambda item
: item
[0]!='_' \
 
  81                             or item
[1]=='_', attributes
) 
  83         attributes 
= filter(lambda item
: item
[:2]!='__', attributes
) 
  86 def hasattrAlwaysReturnsTrue(object): 
  87     return hasattr(object, 'bogu5_123_aTTri8ute') 
  89 def getAllAttributeNames(object): 
  90     """Return dict of all attributes, including inherited, for an object. 
  92     Recursively walk through a class and all base classes. 
  94     attrdict 
= {}  # (object, technique, count): [list of attributes] 
  96     # Do Not use hasattr() as a test anywhere in this function, 
  97     # because it is unreliable with remote objects: xmlrpc, soap, etc. 
  98     # They always return true for hasattr(). 
 101         # This could(?) fail if the type is poorly defined without 
 103         key 
= type(object).__name
__ 
 106     # Wake up sleepy objects - a hack for ZODB objects in "ghost" state. 
 107     wakeupcall 
= dir(object) 
 109     # Get attributes available through the normal convention. 
 110     attributes 
= dir(object) 
 111     attrdict
[(key
, 'dir', len(attributes
))] = attributes
 
 112     # Get attributes from the object's dictionary, if it has one. 
 114         attributes 
= object.__dict
__.keys() 
 116     except:  # Must catch all because object might have __getattr__. 
 119         attrdict
[(key
, '__dict__', len(attributes
))] = attributes
 
 120     # For a class instance, get the attributes for the class. 
 122         klass 
= object.__class
__ 
 123     except:  # Must catch all because object might have __getattr__. 
 127             # Break a circular reference. This happens with extension 
 131             attrdict
.update(getAllAttributeNames(klass
)) 
 132     # Also get attributes from any and all parent classes. 
 134         bases 
= object.__bases
__ 
 135     except:  # Must catch all because object might have __getattr__. 
 138         if isinstance(bases
, types
.TupleType
): 
 140                 if type(base
) is types
.TypeType
: 
 141                     # Break a circular reference. Happens in Python 2.2. 
 144                     attrdict
.update(getAllAttributeNames(base
)) 
 147 def getCallTip(command
='', locals=None): 
 148     """For a command, return a tuple of object name, argspec, tip text. 
 150     The call tip information will be based on the locals namespace.""" 
 151     calltip 
= ('', '', '')  # object name, argspec, tip text. 
 152     # Get the proper chunk of code from the command. 
 153     root 
= getRoot(command
, terminator
='(') 
 155         if locals is not None: 
 156             object = eval(root
, locals) 
 162     object, dropSelf 
= getBaseObject(object) 
 164         name 
= object.__name
__ 
 165     except AttributeError: 
 169     if inspect
.isbuiltin(object): 
 170         # Builtin functions don't have an argspec that we can get. 
 172     elif inspect
.isfunction(object): 
 173         # tip1 is a string like: "getCallTip(command='', locals=None)" 
 174         argspec 
= apply(inspect
.formatargspec
, inspect
.getargspec(object)) 
 176             # The first parameter to a method is a reference to an 
 177             # instance, usually coded as "self", and is usually passed 
 178             # automatically by Python; therefore we want to drop it. 
 179             temp 
= argspec
.split(',') 
 180             if len(temp
) == 1:  # No other arguments. 
 182             elif temp
[0][:2] == '(*': # first param is like *args, not self 
 184             else:  # Drop the first argument. 
 185                 argspec 
= '(' + ','.join(temp
[1:]).lstrip() 
 186         tip1 
= name 
+ argspec
 
 190             doc 
= inspect
.getdoc(object) 
 194         # tip2 is the first separated line of the docstring, like: 
 195         # "Return call tip text for a command." 
 196         # tip3 is the rest of the docstring, like: 
 197         # "The call tip information will be based on ... <snip> 
 198         firstline 
= doc
.split('\n')[0].lstrip() 
 199         if tip1 
== firstline 
or firstline
[:len(name
)+1] == name
+'(': 
 203         docpieces 
= doc
.split('\n\n') 
 205         tip3 
= '\n\n'.join(docpieces
[1:]) 
 206         tip 
= '%s%s\n\n%s' % (tip1
, tip2
, tip3
) 
 209     calltip 
= (name
, argspec
[1:-1], tip
.strip()) 
 212 def getRoot(command
, terminator
=None): 
 213     """Return the rightmost root portion of an arbitrary Python command. 
 215     Return only the root portion that can be eval()'d without side 
 216     effects.  The command would normally terminate with a '(' or 
 217     '.'. The terminator and anything after the terminator will be 
 219     command 
= command
.split('\n')[-1] 
 220     if command
.startswith(sys
.ps2
): 
 221         command 
= command
[len(sys
.ps2
):] 
 222     command 
= command
.lstrip() 
 223     command 
= rtrimTerminus(command
, terminator
) 
 224     tokens 
= getTokens(command
) 
 227     if tokens
[-1][0] is tokenize
.ENDMARKER
: 
 228         # Remove the end marker. 
 232     if terminator 
== '.' and \
 
 233            (tokens
[-1][1] <> '.' or tokens
[-1][0] is not tokenize
.OP
): 
 234         # Trap decimals in numbers, versus the dot operator. 
 237         # Strip off the terminator. 
 238         if terminator 
and command
.endswith(terminator
): 
 239             size 
= 0 - len(terminator
) 
 240             command 
= command
[:size
] 
 241     command 
= command
.rstrip() 
 242     tokens 
= getTokens(command
) 
 248     emptyTypes 
= ('[]', '()', '{}') 
 251         tokenstring 
= token
[1] 
 253         if tokentype 
is tokenize
.ENDMARKER
: 
 255         if tokentype 
in (tokenize
.NAME
, tokenize
.STRING
, tokenize
.NUMBER
) \
 
 256         and laststring 
!= '.': 
 257             # We've reached something that's not part of the root. 
 258             if prefix 
and line
[token
[3][1]] != ' ': 
 259                 # If it doesn't have a space after it, remove the prefix. 
 262         if tokentype 
in (tokenize
.NAME
, tokenize
.STRING
, tokenize
.NUMBER
) \
 
 263         or (tokentype 
is tokenize
.OP 
and tokenstring 
== '.'): 
 265                 # The prefix isn't valid because it comes after a dot. 
 269                 # start represents the last known good point in the line. 
 271         elif len(tokenstring
) == 1 and tokenstring 
in ('[({])}'): 
 272             # Remember, we're working backwords. 
 273             # So prefix += tokenstring would be wrong. 
 274             if prefix 
in emptyTypes 
and tokenstring 
in ('[({'): 
 275                 # We've already got an empty type identified so now we 
 276                 # are in a nested situation and we can break out with 
 280                 prefix 
= tokenstring 
+ prefix
 
 282             # We've reached something that's not part of the root. 
 284         laststring 
= tokenstring
 
 288     if prefix 
in emptyTypes
: 
 289         # Empty types are safe to be eval()'d and introspected. 
 293 def getTokens(command
): 
 294     """Return list of token tuples for command.""" 
 296     # In case the command is unicode try encoding it 
 297     if type(command
) == unicode: 
 299             command 
= command
.encode(wx
.GetDefaultPyEncoding()) 
 300         except UnicodeEncodeError: 
 301             pass # otherwise leave it alone 
 303     f 
= cStringIO
.StringIO(command
) 
 304     # tokens is a list of token tuples, each looking like:  
 305     # (type, string, (srow, scol), (erow, ecol), line) 
 307     # Can't use list comprehension: 
 308     #   tokens = [token for token in tokenize.generate_tokens(f.readline)] 
 309     # because of need to append as much as possible before TokenError. 
 311 ##        This code wasn't backward compatible with Python 2.1.3. 
 313 ##        for token in tokenize.generate_tokens(f.readline): 
 314 ##            tokens.append(token) 
 316         # This works with Python 2.1.3 (with nested_scopes). 
 319         tokenize
.tokenize_loop(f
.readline
, eater
) 
 320     except tokenize
.TokenError
: 
 321         # This is due to a premature EOF, which we expect since we are 
 322         # feeding in fragments of Python code. 
 326 def rtrimTerminus(command
, terminator
=None): 
 327     """Return command minus anything that follows the final terminator.""" 
 329         pieces 
= command
.split(terminator
) 
 331             command 
= terminator
.join(pieces
[:-1]) + terminator
 
 334 def getBaseObject(object): 
 335     """Return base object and dropSelf indicator for an object.""" 
 336     if inspect
.isbuiltin(object): 
 337         # Builtin functions don't have an argspec that we can get. 
 339     elif inspect
.ismethod(object): 
 340         # Get the function from the object otherwise 
 341         # inspect.getargspec() complains that the object isn't a 
 344             if object.im_self 
is None: 
 345                 # This is an unbound method so we do not drop self 
 346                 # from the argspec, since an instance must be passed 
 351             object = object.im_func
 
 352         except AttributeError: 
 354     elif inspect
.isclass(object): 
 355         # Get the __init__ method function for the class. 
 356         constructor 
= getConstructor(object) 
 357         if constructor 
is not None: 
 362     elif callable(object): 
 363         # Get the __call__ method instead. 
 365             object = object.__call
__.im_func
 
 367         except AttributeError: 
 371     return object, dropSelf
 
 373 def getConstructor(object): 
 374     """Return constructor for class object, or None if there isn't one.""" 
 376         return object.__init
__.im_func
 
 377     except AttributeError: 
 378         for base 
in object.__bases
__: 
 379             constructor 
= getConstructor(base
) 
 380             if constructor 
is not None: