docs/tools/codegrab.py
author andy_robinson
Mon, 05 Jun 2000 16:38:53 +0000
changeset 256 0dedfa161b21
child 271 574511abf998
permissions -rw-r--r--
Initial revision
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
        
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    38
        directory = apply(os.path.join, [packagepath] + words[1:-1])
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 = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    57
    for name in dir(mod):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    58
        value = getattr(mod, name)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    59
        if type(value) is types.FunctionType:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    60
            #we're possibly interested in it
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    61
            if os.path.splitext(value.func_code.co_filename)[0] == modulename:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    62
                #it was defined here
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    63
                funcObj = value
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    64
                fn = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    65
                fn.name = name
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    66
                fn.proto = getFunctionPrototype(funcObj, lines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    67
                if funcObj.__doc__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    68
                    fn.doc = dedent(funcObj.__doc__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    69
                else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    70
                    fn.doc = '(no documentation string)'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    71
                result.functions.append(fn)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    72
        elif type(value) == types.ClassType:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    73
            if value.__module__ == modulename:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    74
                cl = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    75
                cl.name = name
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    76
                if value.__doc__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    77
                    cl.doc = dedent(value.__doc__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    78
                else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    79
                    cl.doc = "(no documentation string)"
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    80
            
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    81
                cl.bases = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    82
                for base in value.__bases__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    83
                    cl.bases.append(base.__name__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    84
                
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    85
                cl.methods = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    86
                #loop over dict finding methods defined here
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    87
                # Q - should we show all methods?
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    88
                # loop over dict finding methods defined here
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    89
                items = value.__dict__.items()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    90
                items.sort()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    91
                for (key2, value2) in items:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    92
                    if type(value2) <> types.FunctionType:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    93
                        continue # not a method
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    94
                    elif os.path.splitext(value2.func_code.co_filename)[0] == modulename:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    95
                        continue # defined in base class
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    96
                    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    97
                        #we want it
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    98
                        meth = Struct()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
    99
                        meth.name = key2
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   100
                        meth.proto = getFunctionPrototype(value2, lines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   101
                        if value2.__doc__:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   102
                            meth.doc = dedent(value2.__doc__)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   103
                        else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   104
                            meth.doc = "(no documentation string)"
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   105
                        #is it official?
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   106
                        if key2[0:1] == '_':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   107
                            meth.status = 'private'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   108
                        elif key2[-1] in '0123456789':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   109
                            meth.status = 'experimental'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   110
                        else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   111
                            meth.status = 'official'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   112
                        cl.methods.append(meth)            
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   113
                result.classes.append(cl)                
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   114
    return result
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   115
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   116
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   117
def getFunctionPrototype(f, lines):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   118
    """Pass in the function object and list of lines;
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   119
    it extracts the header as a multiline text block."""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   120
    firstLineNo = f.func_code.co_firstlineno - 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   121
    lineNo = firstLineNo
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   122
    brackets = 0
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   123
    while 1:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   124
        line = lines[lineNo]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   125
        for char in line:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   126
            if char == '(':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   127
                brackets = brackets + 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   128
            elif char == ')':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   129
                brackets = brackets - 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   130
        if brackets == 0:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   131
            break
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   132
        else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   133
            lineNo = lineNo + 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   134
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   135
    usefulLines = lines[firstLineNo:lineNo+1]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   136
    return string.join(usefulLines, '\n')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   137
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   138
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   139
def dedent(comment):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   140
    """Attempts to dedent the lines to the edge. Looks at no.
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   141
    of leading spaces in line 2, and removes up to that number
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   142
    of blanks from other lines."""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   143
    commentLines = string.split(comment, '\n')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   144
    if len(commentLines) < 2:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   145
        cleaned = map(string.lstrip, commentLines)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   146
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   147
        spc = 0
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   148
        for char in commentLines[1]:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   149
            if char in string.whitespace:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   150
                spc = spc + 1
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   151
            else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   152
                break
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   153
        #now check other lines
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   154
        cleaned = []
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   155
        for line in commentLines:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   156
            for i in range(min(len(line),spc)):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   157
                if line[0] in string.whitespace:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   158
                    line = line[1:]
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   159
            cleaned.append(line)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   160
    return string.join(cleaned, '\n')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   161
                           
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   162
    
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   163
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   164
def dumpDoc(modulename, directory=None):
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   165
    """Test support.  Just prints docco on the module
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   166
    to standard output."""
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   167
    docco = getObjectsDefinedIn(modulename, directory)
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   168
    print 'codegrab.py - ReportLab Documentation Utility'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   169
    print 'documenting', modulename + '.py'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   170
    print '-------------------------------------------------------'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   171
    print
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   172
    if docco.functions == []:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   173
        print 'No functions found'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   174
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   175
        print 'Functions:'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   176
        for f in docco.functions:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   177
            print f.proto
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   178
            print '    ' + f.doc
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   179
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   180
    if docco.classes == []:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   181
        print 'No classes found'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   182
    else:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   183
        print 'Classes:'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   184
        for c in docco.classes:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   185
            print c.name
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   186
            print '    ' + c.doc
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   187
            for m in c.methods:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   188
                print m.proto  # it is already indented in the file!
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   189
                print '        ' + m.doc
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   190
            print
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   191
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   192
def test():
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   193
    dumpDoc('reportlab.platypus.paragraph')
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   194
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   195
if __name__=='__main__':
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   196
    import sys
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   197
    print 'Path to search:'
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   198
    for line in sys.path:
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   199
        print '   ',line
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   200
    test()
0dedfa161b21 Initial revision
andy_robinson
parents:
diff changeset
   201