docs/tools/codegrab.py
changeset 256 0dedfa161b21
child 271 574511abf998
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/docs/tools/codegrab.py	Mon Jun 05 16:38:53 2000 +0000
@@ -0,0 +1,201 @@
+#codegrab.py
+"""
+This grabs various Python class, method and function
+headers and their doc strings to include in documents
+"""
+
+import imp
+import types
+import string
+import os
+import sys
+
+class Struct:
+    pass
+
+def getObjectsDefinedIn(modulename, directory=None):
+    """Returns two tuple of (functions, classes) defined
+    in the given module.  'directory' must be the directory
+    containing the script; modulename should not include
+    the .py suffix"""
+
+    if directory:
+        searchpath = [directory]
+    else:
+        searchpath = sys.path   # searches usual Python path
+
+    #might be a package.  If so, check the top level
+    #package is there, then recalculate the path needed
+    words = string.split(modulename, '.')
+    if len(words) > 1:
+        packagename = words[0]
+        packagefound = imp.find_module(packagename, searchpath)
+        assert packagefound, "Package %s not found" % packagename
+        (file, packagepath, description) = packagefound
+        #now the full path should be known, if it is in the
+        #package
+        
+        directory = apply(os.path.join, [packagepath] + words[1:-1])
+        modulename = words[-1]
+        searchpath = [directory]
+
+
+
+    #find and import the module.
+    found = imp.find_module(modulename, searchpath)
+    assert found, "Module %s not found" % modulename
+    (file, pathname, description) = found
+    mod = imp.load_module(modulename, file, pathname, description)
+
+    #grab the code too, minus trailing newlines
+    lines = open(pathname, 'r').readlines()
+    lines = map(string.rstrip, lines)
+    
+    result = Struct()
+    result.functions = []
+    result.classes = []
+    for name in dir(mod):
+        value = getattr(mod, name)
+        if type(value) is types.FunctionType:
+            #we're possibly interested in it
+            if os.path.splitext(value.func_code.co_filename)[0] == modulename:
+                #it was defined here
+                funcObj = value
+                fn = Struct()
+                fn.name = name
+                fn.proto = getFunctionPrototype(funcObj, lines)
+                if funcObj.__doc__:
+                    fn.doc = dedent(funcObj.__doc__)
+                else:
+                    fn.doc = '(no documentation string)'
+                result.functions.append(fn)
+        elif type(value) == types.ClassType:
+            if value.__module__ == modulename:
+                cl = Struct()
+                cl.name = name
+                if value.__doc__:
+                    cl.doc = dedent(value.__doc__)
+                else:
+                    cl.doc = "(no documentation string)"
+            
+                cl.bases = []
+                for base in value.__bases__:
+                    cl.bases.append(base.__name__)
+                
+                cl.methods = []
+                #loop over dict finding methods defined here
+                # Q - should we show all methods?
+                # loop over dict finding methods defined here
+                items = value.__dict__.items()
+                items.sort()
+                for (key2, value2) in items:
+                    if type(value2) <> types.FunctionType:
+                        continue # not a method
+                    elif os.path.splitext(value2.func_code.co_filename)[0] == modulename:
+                        continue # defined in base class
+                    else:
+                        #we want it
+                        meth = Struct()
+                        meth.name = key2
+                        meth.proto = getFunctionPrototype(value2, lines)
+                        if value2.__doc__:
+                            meth.doc = dedent(value2.__doc__)
+                        else:
+                            meth.doc = "(no documentation string)"
+                        #is it official?
+                        if key2[0:1] == '_':
+                            meth.status = 'private'
+                        elif key2[-1] in '0123456789':
+                            meth.status = 'experimental'
+                        else:
+                            meth.status = 'official'
+                        cl.methods.append(meth)            
+                result.classes.append(cl)                
+    return result
+
+
+def getFunctionPrototype(f, lines):
+    """Pass in the function object and list of lines;
+    it extracts the header as a multiline text block."""
+    firstLineNo = f.func_code.co_firstlineno - 1
+    lineNo = firstLineNo
+    brackets = 0
+    while 1:
+        line = lines[lineNo]
+        for char in line:
+            if char == '(':
+                brackets = brackets + 1
+            elif char == ')':
+                brackets = brackets - 1
+        if brackets == 0:
+            break
+        else:
+            lineNo = lineNo + 1
+
+    usefulLines = lines[firstLineNo:lineNo+1]
+    return string.join(usefulLines, '\n')
+
+
+def dedent(comment):
+    """Attempts to dedent the lines to the edge. Looks at no.
+    of leading spaces in line 2, and removes up to that number
+    of blanks from other lines."""
+    commentLines = string.split(comment, '\n')
+    if len(commentLines) < 2:
+        cleaned = map(string.lstrip, commentLines)
+    else:
+        spc = 0
+        for char in commentLines[1]:
+            if char in string.whitespace:
+                spc = spc + 1
+            else:
+                break
+        #now check other lines
+        cleaned = []
+        for line in commentLines:
+            for i in range(min(len(line),spc)):
+                if line[0] in string.whitespace:
+                    line = line[1:]
+            cleaned.append(line)
+    return string.join(cleaned, '\n')
+                           
+    
+
+def dumpDoc(modulename, directory=None):
+    """Test support.  Just prints docco on the module
+    to standard output."""
+    docco = getObjectsDefinedIn(modulename, directory)
+    print 'codegrab.py - ReportLab Documentation Utility'
+    print 'documenting', modulename + '.py'
+    print '-------------------------------------------------------'
+    print
+    if docco.functions == []:
+        print 'No functions found'
+    else:
+        print 'Functions:'
+        for f in docco.functions:
+            print f.proto
+            print '    ' + f.doc
+
+    if docco.classes == []:
+        print 'No classes found'
+    else:
+        print 'Classes:'
+        for c in docco.classes:
+            print c.name
+            print '    ' + c.doc
+            for m in c.methods:
+                print m.proto  # it is already indented in the file!
+                print '        ' + m.doc
+            print
+
+def test():
+    dumpDoc('reportlab.platypus.paragraph')
+
+if __name__=='__main__':
+    import sys
+    print 'Path to search:'
+    for line in sys.path:
+        print '   ',line
+    test()
+