introspect.py :  » Game-2D-3D » PsychoPy » PsychoPy-0.96.02 » PsychoPyIDE » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Game 2D 3D » PsychoPy 
PsychoPy » PsychoPy 0.96.02 » PsychoPyIDE » introspect.py
"""Provides a variety of introspective-type support functions for
things like call tips and command auto completion."""

__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>"
__cvsid__ = "$Id: introspect.py,v 1.15 2006/06/29 22:23:19 RD Exp $"
__revision__ = "$Revision: 1.15 $"[11:-2]

import cStringIO
import inspect
import sys
import tokenize
import types
import wx

def getAutoCompleteList(command='', locals=None, includeMagic=1, 
                        includeSingle=1, includeDouble=1):
    """Return list of auto-completion options for command.
    
    The list of options will be based on the locals namespace."""
    attributes = []
    # Get the proper chunk of code from the command.
    root = getRoot(command, terminator='.')
    try:
        if locals is not None:
            object = eval(root, locals)
        else:
            object = eval(root)
    except:
        pass
    else:
        attributes = getAttributeNames(object, includeMagic, 
                                       includeSingle, includeDouble)
    return attributes
    
def getAttributeNames(object, includeMagic=1, includeSingle=1,
                      includeDouble=1):
    """Return list of unique attributes, including inherited, for object."""
    attributes = []
    dict = {}
    if not hasattrAlwaysReturnsTrue(object):
        # Add some attributes that don't always get picked up.
        special_attrs = ['__bases__', '__class__', '__dict__', '__name__',
                         'func_closure', 'func_code', 'func_defaults',
                         'func_dict', 'func_doc', 'func_globals', 'func_name']
        attributes += [attr for attr in special_attrs \
                       if hasattr(object, attr)]
    if includeMagic:
        try: attributes += object._getAttributeNames()
        except: pass
    # Get all attribute names.
    str_type = str(type(object))
    if str_type == "<type 'array'>":
        attributes += dir(object)
    else:
        attrdict = getAllAttributeNames(object)
        # Store the object's dir.
        object_dir = dir(object)
        for (obj_type_name, technique, count), attrlist in attrdict.items():
            # This complexity is necessary to avoid accessing all the
            # attributes of the object.  This is very handy for objects
            # whose attributes are lazily evaluated.
            if type(object).__name__ == obj_type_name and technique == 'dir':
                attributes += attrlist
            else:
                attributes += [attr for attr in attrlist \
                               if attr not in object_dir and hasattr(object, attr)]
            
    # Remove duplicates from the attribute list.
    for item in attributes:
        dict[item] = None
    attributes = dict.keys()
    # new-style swig wrappings can result in non-string attributes
    # e.g. ITK http://www.itk.org/
    attributes = [attribute for attribute in attributes \
                  if type(attribute) == str]
    attributes.sort(lambda x, y: cmp(x.upper(), y.upper()))
    if not includeSingle:
        attributes = filter(lambda item: item[0]!='_' \
                            or item[1:2]=='_', attributes)
    if not includeDouble:
        attributes = filter(lambda item: item[:2]!='__', attributes)
    return attributes

def hasattrAlwaysReturnsTrue(object):
    return hasattr(object, 'bogu5_123_aTTri8ute')

def getAllAttributeNames(object):
    """Return dict of all attributes, including inherited, for an object.
    
    Recursively walk through a class and all base classes.
    """
    attrdict = {}  # (object, technique, count): [list of attributes]
    # !!!
    # Do Not use hasattr() as a test anywhere in this function,
    # because it is unreliable with remote objects: xmlrpc, soap, etc.
    # They always return true for hasattr().
    # !!!
    try:
        # This could(?) fail if the type is poorly defined without
        # even a name.
        key = type(object).__name__
    except:
        key = 'anonymous'
    # Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
    wakeupcall = dir(object)
    del wakeupcall
    # Get attributes available through the normal convention.
    attributes = dir(object)
    attrdict[(key, 'dir', len(attributes))] = attributes
    # Get attributes from the object's dictionary, if it has one.
    try:
        attributes = object.__dict__.keys()
        attributes.sort()
    except:  # Must catch all because object might have __getattr__.
        pass
    else:
        attrdict[(key, '__dict__', len(attributes))] = attributes
    # For a class instance, get the attributes for the class.
    try:
        klass = object.__class__
    except:  # Must catch all because object might have __getattr__.
        pass
    else:
        if klass is object:
            # Break a circular reference. This happens with extension
            # classes.
            pass
        else:
            attrdict.update(getAllAttributeNames(klass))
    # Also get attributes from any and all parent classes.
    try:
        bases = object.__bases__
    except:  # Must catch all because object might have __getattr__.
        pass
    else:
        if isinstance(bases, types.TupleType):
            for base in bases:
                if type(base) is types.TypeType:
                    # Break a circular reference. Happens in Python 2.2.
                    pass
                else:
                    attrdict.update(getAllAttributeNames(base))
    return attrdict

def getCallTip(command='', locals=None):
    """For a command, return a tuple of object name, argspec, tip text.
    
    The call tip information will be based on the locals namespace."""
    calltip = ('', '', '')  # object name, argspec, tip text.
    # Get the proper chunk of code from the command.
    root = getRoot(command, terminator='(')
    try:
        if locals is not None:
            object = eval(root, locals)
        else:
            object = eval(root)
    except:
        return calltip
    name = ''
    object, dropSelf = getBaseObject(object)
    try:
        name = object.__name__
    except AttributeError:
        pass
    tip1 = ''
    argspec = ''
    if inspect.isbuiltin(object):
        # Builtin functions don't have an argspec that we can get.
        pass
    elif inspect.isfunction(object):
        # tip1 is a string like: "getCallTip(command='', locals=None)"
        argspec = apply(inspect.formatargspec, inspect.getargspec(object))
        if dropSelf:
            # The first parameter to a method is a reference to an
            # instance, usually coded as "self", and is usually passed
            # automatically by Python; therefore we want to drop it.
            temp = argspec.split(',')
            if len(temp) == 1:  # No other arguments.
                argspec = '()'
            elif temp[0][:2] == '(*': # first param is like *args, not self
                pass 
            else:  # Drop the first argument.
                argspec = '(' + ','.join(temp[1:]).lstrip()
        tip1 = name + argspec
    doc = ''
    if callable(object):
        try:
            doc = inspect.getdoc(object)
        except:
            pass
    if doc:
        # tip2 is the first separated line of the docstring, like:
        # "Return call tip text for a command."
        # tip3 is the rest of the docstring, like:
        # "The call tip information will be based on ... <snip>
        firstline = doc.split('\n')[0].lstrip()
        if tip1 == firstline or firstline[:len(name)+1] == name+'(':
            tip1 = ''
        else:
            tip1 += '\n\n'
        docpieces = doc.split('\n\n')
        tip2 = docpieces[0]
        tip3 = '\n\n'.join(docpieces[1:])
        tip = '%s%s\n\n%s' % (tip1, tip2, tip3)
    else:
        tip = tip1
    #calltip = (name, argspec[1:-1], tip.strip())
    calltip = (name, argspec, tip.strip())
    return calltip

def getRoot(command, terminator=None):
    """Return the rightmost root portion of an arbitrary Python command.
    
    Return only the root portion that can be eval()'d without side
    effects.  The command would normally terminate with a '(' or
    '.'. The terminator and anything after the terminator will be
    dropped."""
    command = command.split('\n')[-1]
    #if command.startswith(sys.ps2): #remove epsilon (... )  # removed by jwp - only works for interactive window
        #command = command[len(sys.ps2):]
    command = command.lstrip()
    command = rtrimTerminus(command, terminator)
    tokens = getTokens(command)
    if not tokens:
        return ''
    if tokens[-1][0] is tokenize.ENDMARKER:
        # Remove the end marker.
        del tokens[-1]
    if not tokens:
        return ''
    if terminator == '.' and \
           (tokens[-1][1] <> '.' or tokens[-1][0] is not tokenize.OP):
        # Trap decimals in numbers, versus the dot operator.
        return ''
    else:
        # Strip off the terminator.
        if terminator and command.endswith(terminator):
            size = 0 - len(terminator)
            command = command[:size]
    command = command.rstrip()
    tokens = getTokens(command)
    tokens.reverse()
    line = ''
    start = None
    prefix = ''
    laststring = '.'
    emptyTypes = ('[]', '()', '{}')
    for token in tokens:
        tokentype = token[0]
        tokenstring = token[1]
        line = token[4]
        if tokentype is tokenize.ENDMARKER:
            continue
        if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
        and laststring != '.':
            # We've reached something that's not part of the root.
            if prefix and line[token[3][1]] != ' ':
                # If it doesn't have a space after it, remove the prefix.
                prefix = ''
            break
        if tokentype in (tokenize.NAME, tokenize.STRING, tokenize.NUMBER) \
        or (tokentype is tokenize.OP and tokenstring == '.'):
            if prefix:
                # The prefix isn't valid because it comes after a dot.
                prefix = ''
                break
            else:
                # start represents the last known good point in the line.
                start = token[2][1]
        elif len(tokenstring) == 1 and tokenstring in ('[({])}'):
            # Remember, we're working backwords.
            # So prefix += tokenstring would be wrong.
            if prefix in emptyTypes and tokenstring in ('[({'):
                # We've already got an empty type identified so now we
                # are in a nested situation and we can break out with
                # what we've got.
                break
            else:
                prefix = tokenstring + prefix
        else:
            # We've reached something that's not part of the root.
            break
        laststring = tokenstring
    if start is None:
        start = len(line)
    root = line[start:]
    if prefix in emptyTypes:
        # Empty types are safe to be eval()'d and introspected.
        root = prefix + root
    return root    

def getTokens(command):
    """Return list of token tuples for command."""

    # In case the command is unicode try encoding it
    if type(command) == unicode:
        try:
            command = command.encode(wx.GetDefaultPyEncoding())
        except UnicodeEncodeError:
            pass # otherwise leave it alone
                
    f = cStringIO.StringIO(command)
    # tokens is a list of token tuples, each looking like: 
    # (type, string, (srow, scol), (erow, ecol), line)
    tokens = []
    # Can't use list comprehension:
    #   tokens = [token for token in tokenize.generate_tokens(f.readline)]
    # because of need to append as much as possible before TokenError.
    try:
##        This code wasn't backward compatible with Python 2.1.3.
##
##        for token in tokenize.generate_tokens(f.readline):
##            tokens.append(token)

        # This works with Python 2.1.3 (with nested_scopes).
        def eater(*args):
            tokens.append(args)
        tokenize.tokenize_loop(f.readline, eater)
    except tokenize.TokenError:
        # This is due to a premature EOF, which we expect since we are
        # feeding in fragments of Python code.
        pass
    return tokens    

def rtrimTerminus(command, terminator=None):
    """Return command minus anything that follows the final terminator."""
    if terminator:
        pieces = command.split(terminator)
        if len(pieces) > 1:
            command = terminator.join(pieces[:-1]) + terminator
    return command

def getBaseObject(object):
    """Return base object and dropSelf indicator for an object."""
    if inspect.isbuiltin(object):
        # Builtin functions don't have an argspec that we can get.
        dropSelf = 0
    elif inspect.ismethod(object):
        # Get the function from the object otherwise
        # inspect.getargspec() complains that the object isn't a
        # Python function.
        try:
            if object.im_self is None:
                # This is an unbound method so we do not drop self
                # from the argspec, since an instance must be passed
                # as the first arg.
                dropSelf = 0
            else:
                dropSelf = 1
            object = object.im_func
        except AttributeError:
            dropSelf = 0
    elif inspect.isclass(object):
        # Get the __init__ method function for the class.
        constructor = getConstructor(object)
        if constructor is not None:
            object = constructor
            dropSelf = 1
        else:
            dropSelf = 0
    elif callable(object):
        # Get the __call__ method instead.
        try:
            object = object.__call__.im_func
            dropSelf = 1
        except AttributeError:
            dropSelf = 0
    else:
        dropSelf = 0
    return object, dropSelf

def getConstructor(object):
    """Return constructor for class object, or None if there isn't one."""
    try:
        return object.__init__.im_func
    except AttributeError:
        for base in object.__bases__:
            constructor = getConstructor(base)
            if constructor is not None:
                return constructor
    return None
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.