describe.py 3.98 KB
Newer Older
Ole Streicher's avatar
Ole Streicher committed
1 2 3
# http://www.dejanews.com/getdoc.xp?AN=382948703
#
# Instant Python
4
# $Id$
Ole Streicher's avatar
Ole Streicher committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
#
# utilities to describe functions, methods, and classes
#
# history:
# 96-10-27 fl     created
# 98-02-24 fl     added code to handle unpacked arguments
# 01-11-13 rlw    added UNPACK_SEQUENCE to UNPACK_TUPLE for tuple args
#                 (Changed for Python2.0)
#
# notes:
# This has been tested with Python 1.4 and 1.5.  The code and
# function object attributes might change in future versions of
# Python.
#
# written by fredrik lundh.  last updated february 1998.
#
# fredrik@pythonware.com
# http://www.pythonware.com
#

from __future__ import division # confidence high

from dis import opname, HAVE_ARGUMENT

# --------------------------------------------------------------------
# code object attributes
# --------------------------------------------------------------------
# co_argcount   INT
# co_nlocals    INT
# co_flags      INT
#       CO_OPTIMIZED
#       CO_NEWLOCALS
#       CO_VARARGS
#       CO_VARKEYWORDS
# co_code       OBJECT
# co_consts     OBJECT
# co_names      OBJECT
# co_varnames   OBJECT
# co_filename   OBJECT
# co_name       OBJECT

# --------------------------------------------------------------------
# function object attributes
# --------------------------------------------------------------------
# func_code     OBJECT
# func_globals  OBJECT
# func_name     OBJECT (__name__)
# func_defaults OBJECT
# func_doc      OBJECT (__doc__)

# copied from Python header file
CO_OPTIMIZED = 0x0001
CO_NEWLOCALS = 0x0002
CO_VARARGS = 0x0004
CO_VARKEYWORDS = 0x0008

def describeParams(func, name = None):
    # get argument list

    code = func.func_code

    n = code.co_argcount
    a = list(code.co_varnames[:n])
    p = 0
    for i in range(n):
        # anonymous arguments
        c = code.co_code
        if not a[i] or a[i][0] == ".":
            vars = []
            while p < len(c):
                v = ord(c[p])
                if v >= HAVE_ARGUMENT:
                    s, v = opname[v], ord(c[p+1]) + ord(c[p+2])*256
                    p = p + 3
                    if s in ("UNPACK_SEQUENCE", "UNPACK_TUPLE"):
                        count = v
                    elif s == "STORE_FAST":
                        vars.append(code.co_varnames[v])
                        if len(vars) >= count:
                            break
                else:
                    p = p + 1
            if vars:
                a[i] = "(" + ", ".join(vars) + ")"
    if func.func_defaults:
        # defaults
        i = n - len(func.func_defaults)
        for d in func.func_defaults:
            a[i] = (a[i], d)
            i = i + 1
    if code.co_flags & CO_VARARGS:
        # extra arguments
        a.append("*"+code.co_varnames[n])
        n = n + 1
    if code.co_flags & CO_VARKEYWORDS:
        # extra keyword arguments
        a.append("**"+code.co_varnames[n])
        n = n + 1
    return a

def describe(func, name = None):
    "Return the function or method declaration as a string"

    # argument list
    a = describeParams(func)
    args = []
    for arg in a:
        if type(arg) == type(""):
            args.append(arg)
        else:
            args.append("%s=%s" % (arg[0], repr(arg[1])))
    args = ", ".join(args)

    # function name
    if not name:
        # func_name is considered obsolete, use __name__ instead
        # name = func.func_name
        name = func.__name__
        if name == "<lambda>":
            return "lambda %s" % args
    return "%s(%s)" % (name, args)

def __getmethods(c, m):
    for k, v in c.__dict__.items():
        if type(v) == type(__getmethods): # and k[0] != "_":
130
            if k not in m:
Ole Streicher's avatar
Ole Streicher committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
                m[k] = describe(v, k), c.__name__
    for c in c.__bases__:
        __getmethods(c, m)

def describe_class(cls):
    "Return a dictionary describing all methods available in a class"

    m = {}
    __getmethods(cls, m)
    return m

def describe_instance(self):
    "Return a dictionary describing all methods available in an instance"

    return describe_class(self.__class__)