docs/tools/codegrab.py
author rgbecker
Tue, 20 Jun 2000 11:35:08 +0000
changeset 287 bbd0eb35aee8
parent 271 574511abf998
child 310 cbec783cfb81
permissions -rw-r--r--
Python 1.5.1 compatibility fixes
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     1
#codegrab.py
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     2
"""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     3
This grabs various Python class, method and function
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     4
headers and their doc strings to include in documents
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     5
"""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     6
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     7
import imp
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     8
import types
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
     9
import string
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    10
import os
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    11
import sys
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    12
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    13
class Struct:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    14
    pass
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    15
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    16
def getObjectsDefinedIn(modulename, directory=None):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    17
    """Returns two tuple of (functions, classes) defined
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    18
    in the given module.  'directory' must be the directory
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    19
    containing the script; modulename should not include
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    20
    the .py suffix"""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    21
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    22
    if directory:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    23
        searchpath = [directory]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    24
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    25
        searchpath = sys.path   # searches usual Python path
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    26
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    27
    #might be a package.  If so, check the top level
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    28
    #package is there, then recalculate the path needed
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    29
    words = string.split(modulename, '.')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    30
    if len(words) > 1:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    31
        packagename = words[0]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    32
        packagefound = imp.find_module(packagename, searchpath)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    33
        assert packagefound, "Package %s not found" % packagename
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    34
        (file, packagepath, description) = packagefound
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    35
        #now the full path should be known, if it is in the
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    36
        #package
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    37
        
287
bbd0eb35aee8 Python 1.5.1 compatibility fixes
rgbecker
parents: 271
diff changeset
    38
        directory = apply(os.path.join, tuple([packagepath] + words[1:-1]))
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    39
        modulename = words[-1]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    40
        searchpath = [directory]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    41
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    42
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    43
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    44
    #find and import the module.
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    45
    found = imp.find_module(modulename, searchpath)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    46
    assert found, "Module %s not found" % modulename
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    47
    (file, pathname, description) = found
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    48
    mod = imp.load_module(modulename, file, pathname, description)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    49
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    50
    #grab the code too, minus trailing newlines
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    51
    lines = open(pathname, 'r').readlines()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    52
    lines = map(string.rstrip, lines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    53
    
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    54
    result = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    55
    result.functions = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    56
    result.classes = []
271
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    57
    result.doc = mod.__doc__
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    58
    for name in dir(mod):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    59
        value = getattr(mod, name)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    60
        if type(value) is types.FunctionType:
271
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    61
            path, file = os.path.split(value.func_code.co_filename)
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    62
            root, ext = os.path.splitext(file)
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    63
            #we're possibly interested in it
271
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    64
            if root == modulename:
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    65
                #it was defined here
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    66
                funcObj = value
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    67
                fn = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    68
                fn.name = name
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    69
                fn.proto = getFunctionPrototype(funcObj, lines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    70
                if funcObj.__doc__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    71
                    fn.doc = dedent(funcObj.__doc__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    72
                else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    73
                    fn.doc = '(no documentation string)'
271
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    74
                #is it official?
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    75
                if name[0:1] == '_':
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    76
                     fn.status = 'private'
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    77
                elif name[-1] in '0123456789':
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    78
                    fn.status = 'experimental'
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    79
                else:
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    80
                    fn.status = 'official'
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    81
                        
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    82
                result.functions.append(fn)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    83
        elif type(value) == types.ClassType:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    84
            if value.__module__ == modulename:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    85
                cl = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    86
                cl.name = name
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    87
                if value.__doc__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    88
                    cl.doc = dedent(value.__doc__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    89
                else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    90
                    cl.doc = "(no documentation string)"
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    91
            
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    92
                cl.bases = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    93
                for base in value.__bases__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    94
                    cl.bases.append(base.__name__)
271
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    95
                if name[0:1] == '_':
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    96
                    cl.status = 'private'
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    97
                elif name[-1] in '0123456789':
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    98
                    cl.status = 'experimental'
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
    99
                else:
574511abf998 Added docs for library
andy_robinson
parents: 256
diff changeset
   100
                    cl.status = 'official'
256
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   101
                
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   102
                cl.methods = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   103
                #loop over dict finding methods defined here
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   104
                # Q - should we show all methods?
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   105
                # loop over dict finding methods defined here
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   106
                items = value.__dict__.items()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   107
                items.sort()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   108
                for (key2, value2) in items:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   109
                    if type(value2) <> types.FunctionType:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   110
                        continue # not a method
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   111
                    elif os.path.splitext(value2.func_code.co_filename)[0] == modulename:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   112
                        continue # defined in base class
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   113
                    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   114
                        #we want it
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   115
                        meth = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   116
                        meth.name = key2
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   117
                        meth.proto = getFunctionPrototype(value2, lines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   118
                        if value2.__doc__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   119
                            meth.doc = dedent(value2.__doc__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   120
                        else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   121
                            meth.doc = "(no documentation string)"
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   122
                        #is it official?
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   123
                        if key2[0:1] == '_':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   124
                            meth.status = 'private'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   125
                        elif key2[-1] in '0123456789':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   126
                            meth.status = 'experimental'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   127
                        else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   128
                            meth.status = 'official'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   129
                        cl.methods.append(meth)            
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   130
                result.classes.append(cl)                
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   131
    return result
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   132
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   133
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   134
def getFunctionPrototype(f, lines):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   135
    """Pass in the function object and list of lines;
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   136
    it extracts the header as a multiline text block."""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   137
    firstLineNo = f.func_code.co_firstlineno - 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   138
    lineNo = firstLineNo
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   139
    brackets = 0
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   140
    while 1:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   141
        line = lines[lineNo]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   142
        for char in line:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   143
            if char == '(':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   144
                brackets = brackets + 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   145
            elif char == ')':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   146
                brackets = brackets - 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   147
        if brackets == 0:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   148
            break
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   149
        else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   150
            lineNo = lineNo + 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   151
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   152
    usefulLines = lines[firstLineNo:lineNo+1]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   153
    return string.join(usefulLines, '\n')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   154
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   155
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   156
def dedent(comment):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   157
    """Attempts to dedent the lines to the edge. Looks at no.
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   158
    of leading spaces in line 2, and removes up to that number
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   159
    of blanks from other lines."""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   160
    commentLines = string.split(comment, '\n')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   161
    if len(commentLines) < 2:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   162
        cleaned = map(string.lstrip, commentLines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   163
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   164
        spc = 0
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   165
        for char in commentLines[1]:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   166
            if char in string.whitespace:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   167
                spc = spc + 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   168
            else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   169
                break
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   170
        #now check other lines
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   171
        cleaned = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   172
        for line in commentLines:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   173
            for i in range(min(len(line),spc)):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   174
                if line[0] in string.whitespace:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   175
                    line = line[1:]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   176
            cleaned.append(line)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   177
    return string.join(cleaned, '\n')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   178
                           
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   179
    
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   180
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   181
def dumpDoc(modulename, directory=None):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   182
    """Test support.  Just prints docco on the module
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   183
    to standard output."""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   184
    docco = getObjectsDefinedIn(modulename, directory)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   185
    print 'codegrab.py - ReportLab Documentation Utility'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   186
    print 'documenting', modulename + '.py'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   187
    print '-------------------------------------------------------'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   188
    print
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   189
    if docco.functions == []:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   190
        print 'No functions found'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   191
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   192
        print 'Functions:'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   193
        for f in docco.functions:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   194
            print f.proto
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   195
            print '    ' + f.doc
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   196
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   197
    if docco.classes == []:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   198
        print 'No classes found'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   199
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   200
        print 'Classes:'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   201
        for c in docco.classes:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   202
            print c.name
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   203
            print '    ' + c.doc
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   204
            for m in c.methods:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   205
                print m.proto  # it is already indented in the file!
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   206
                print '        ' + m.doc
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   207
            print
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   208
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   209
def test():
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   210
    dumpDoc('reportlab.platypus.paragraph')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   211
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   212
if __name__=='__main__':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   213
    import sys
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   214
    print 'Path to search:'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   215
    for line in sys.path:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   216
        print '   ',line
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   217
    test()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   218