diff --git a/bin/reportlab/tools/README b/bin/reportlab/tools/README
new file mode 100644
index 00000000000..f5f75d08de3
--- /dev/null
+++ b/bin/reportlab/tools/README
@@ -0,0 +1,10 @@
+This directory is the home of various ReportLab tools.
+They are packaged such that they can be used more easily.
+
+Tool candidates are:
+
+ - PythonPoint
+ - docpy.py
+ - graphdocpy.py
+ - ...
+
diff --git a/bin/reportlab/tools/__init__.py b/bin/reportlab/tools/__init__.py
new file mode 100644
index 00000000000..da52b52d322
--- /dev/null
+++ b/bin/reportlab/tools/__init__.py
@@ -0,0 +1,3 @@
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/__init__.py
diff --git a/bin/reportlab/tools/docco/README b/bin/reportlab/tools/docco/README
new file mode 100644
index 00000000000..b23b5661348
--- /dev/null
+++ b/bin/reportlab/tools/docco/README
@@ -0,0 +1,8 @@
+This directory contains a number of tools to do with documentation and
+documentation building.
+
+Some of these are our own internal tools which we use for building the
+ReportLab documentation. Some tools are obsolete and will be removed
+in due course.
+
+In the mean time, use at your own risk.
diff --git a/bin/reportlab/tools/docco/__init__.py b/bin/reportlab/tools/docco/__init__.py
new file mode 100644
index 00000000000..3a0b73da24d
--- /dev/null
+++ b/bin/reportlab/tools/docco/__init__.py
@@ -0,0 +1,3 @@
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/docco/__init__.py
diff --git a/bin/reportlab/tools/docco/codegrab.py b/bin/reportlab/tools/docco/codegrab.py
new file mode 100644
index 00000000000..87a580a2a3e
--- /dev/null
+++ b/bin/reportlab/tools/docco/codegrab.py
@@ -0,0 +1,228 @@
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/docco/codegrab.py
+#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, tuple([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 = []
+ result.doc = mod.__doc__
+ for name in dir(mod):
+ value = getattr(mod, name)
+ if type(value) is types.FunctionType:
+ path, file = os.path.split(value.func_code.co_filename)
+ root, ext = os.path.splitext(file)
+ #we're possibly interested in it
+ if root == 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)'
+ #is it official?
+ if name[0:1] == '_':
+ fn.status = 'private'
+ elif name[-1] in '0123456789':
+ fn.status = 'experimental'
+ else:
+ fn.status = 'official'
+
+ 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__)
+ if name[0:1] == '_':
+ cl.status = 'private'
+ elif name[-1] in '0123456789':
+ cl.status = 'experimental'
+ else:
+ cl.status = 'official'
+
+ 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
+ name2 = value2.func_code.co_name
+ meth.proto = getFunctionPrototype(value2, lines)
+ if name2!=key2:
+ meth.doc = 'pointer to '+name2
+ meth.proto = string.replace(meth.proto,name2,key2)
+ else:
+ 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(m='reportlab.platypus.paragraph'):
+ dumpDoc(m)
+
+if __name__=='__main__':
+ import sys
+ print 'Path to search:'
+ for line in sys.path:
+ print ' ',line
+ M = sys.argv[1:]
+ if M==[]:
+ M.append('reportlab.platypus.paragraph')
+ for m in M:
+ test(m)
\ No newline at end of file
diff --git a/bin/reportlab/tools/docco/docpy.py b/bin/reportlab/tools/docco/docpy.py
new file mode 100644
index 00000000000..75a77efe773
--- /dev/null
+++ b/bin/reportlab/tools/docco/docpy.py
@@ -0,0 +1,1247 @@
+#!/usr/bin/env python
+#Copyright ReportLab Europe Ltd. 2000-2004
+#see license.txt for license details
+#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/tools/docco/docpy.py
+
+"""Generate documentation from live Python objects.
+
+This is an evolving module that allows to generate documentation
+for python modules in an automated fashion. The idea is to take
+live Python objects and inspect them in order to use as much mean-
+ingful information as possible to write in some formatted way into
+different types of documents.
+
+In principle a skeleton captures the gathered information and
+makes it available via a certain API to formatters that use it
+in whatever way they like to produce something of interest. The
+API allows for adding behaviour in subclasses of these formatters,
+such that, e.g. for certain classes it is possible to trigger
+special actions like displaying a sample image of a class that
+represents some graphical widget, say.
+
+Type the following for usage info:
+
+ python docpy.py -h
+"""
+
+# Much inspired by Ka-Ping Yee's htmldoc.py.
+# Needs the inspect module.
+
+# Dinu Gherman
+
+
+__version__ = '0.8'
+
+
+import sys, os, re, types, string, getopt, copy, time
+from string import find, join, split, replace, expandtabs, rstrip
+
+from reportlab.pdfgen import canvas
+from reportlab.lib import colors
+from reportlab.lib.units import inch, cm
+from reportlab.lib.pagesizes import A4
+from reportlab.lib import enums
+from reportlab.lib.enums import TA_CENTER, TA_LEFT
+from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
+from reportlab.platypus.flowables import Flowable, Spacer
+from reportlab.platypus.paragraph import Paragraph
+from reportlab.platypus.flowables \
+ import Flowable, Preformatted,Spacer, Image, KeepTogether, PageBreak
+from reportlab.platypus.tableofcontents import TableOfContents
+from reportlab.platypus.xpreformatted import XPreformatted
+from reportlab.platypus.frames import Frame
+from reportlab.platypus.doctemplate \
+ import PageTemplate, BaseDocTemplate
+from reportlab.platypus.tables import TableStyle, Table
+import inspect
+
+####################################################################
+#
+# Stuff needed for building PDF docs.
+#
+####################################################################
+
+
+def mainPageFrame(canvas, doc):
+ "The page frame used for all PDF documents."
+
+ canvas.saveState()
+
+ pageNumber = canvas.getPageNumber()
+ canvas.line(2*cm, A4[1]-2*cm, A4[0]-2*cm, A4[1]-2*cm)
+ canvas.line(2*cm, 2*cm, A4[0]-2*cm, 2*cm)
+ if pageNumber > 1:
+ canvas.setFont('Times-Roman', 12)
+ canvas.drawString(4 * inch, cm, "%d" % pageNumber)
+ if hasattr(canvas, 'headerLine'): # hackish
+ headerline = string.join(canvas.headerLine, ' \215 ') # bullet
+ canvas.drawString(2*cm, A4[1]-1.75*cm, headerline)
+
+ canvas.setFont('Times-Roman', 8)
+ msg = "Generated with reportlab.lib.docpy. See http://www.reportlab.com!"
+ canvas.drawString(2*cm, 1.65*cm, msg)
+
+ canvas.restoreState()
+
+
+class MyTemplate(BaseDocTemplate):
+ "The document template used for all PDF documents."
+
+ _invalidInitArgs = ('pageTemplates',)
+
+ def __init__(self, filename, **kw):
+ frame1 = Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id='F1')
+ self.allowSplitting = 0
+ apply(BaseDocTemplate.__init__, (self, filename), kw)
+ self.addPageTemplates(PageTemplate('normal', [frame1], mainPageFrame))
+
+
+ def afterFlowable(self, flowable):
+ "Takes care of header line, TOC and outline entries."
+
+ if flowable.__class__.__name__ == 'Paragraph':
+ f = flowable
+ name7 = f.style.name[:7]
+ name8 = f.style.name[:8]
+
+ # Build a list of heading parts.
+ # So far, this is the *last* item on the *previous* page...
+ if name7 == 'Heading' and not hasattr(self.canv, 'headerLine'):
+ self.canv.headerLine = []
+
+ if name8 == 'Heading0':
+ self.canv.headerLine = [f.text] # hackish
+ elif name8 == 'Heading1':
+ if len(self.canv.headerLine) == 2:
+ del self.canv.headerLine[-1]
+ elif len(self.canv.headerLine) == 3:
+ del self.canv.headerLine[-1]
+ del self.canv.headerLine[-1]
+ self.canv.headerLine.append(f.text)
+ elif name8 == 'Heading2':
+ if len(self.canv.headerLine) == 3:
+ del self.canv.headerLine[-1]
+ self.canv.headerLine.append(f.text)
+
+ if name7 == 'Heading':
+ # Register TOC entries.
+ headLevel = int(f.style.name[7:])
+ self.notify('TOCEntry', (headLevel, flowable.getPlainText(), self.page))
+
+ # Add PDF outline entries.
+ c = self.canv
+ title = f.text
+ key = str(hash(f))
+
+ try:
+ if headLevel == 0:
+ isClosed = 0
+ else:
+ isClosed = 1
+
+ c.bookmarkPage(key)
+ c.addOutlineEntry(title, key, level=headLevel,
+ closed=isClosed)
+ except ValueError:
+ pass
+
+
+####################################################################
+#
+# Utility functions (Ka-Ping Yee).
+#
+####################################################################
+
+def htmlescape(text):
+ "Escape special HTML characters, namely &, <, >."
+ return replace(replace(replace(text, '&', '&'),
+ '<', '<'),
+ '>', '>')
+
+def htmlrepr(object):
+ return htmlescape(repr(object))
+
+
+def defaultformat(object):
+ return '=' + htmlrepr(object)
+
+
+def getdoc(object):
+ result = inspect.getdoc(object)
+ if not result:
+ try:
+ result = inspect.getcomments(object)
+ except:
+ pass
+ return result and rstrip(result) + '\n' or ''
+
+
+def reduceDocStringLength(docStr):
+ "Return first line of a multiline string."
+
+ return split(docStr, '\n')[0]
+
+
+####################################################################
+#
+# More utility functions
+#
+####################################################################
+
+def makeHtmlSection(text, bgcolor='#FFA0FF'):
+ """Create HTML code for a section.
+
+ This is usually a header for all classes or functions.
+u """
+ text = htmlescape(expandtabs(text))
+ result = []
+ result.append("""
""")
+ result.append("""""" % bgcolor)
+ result.append("""%s""" % text)
+ result.append(""" |
""")
+ result.append('')
+
+ return join(result, '\n')
+
+
+def makeHtmlSubSection(text, bgcolor='#AAA0FF'):
+ """Create HTML code for a subsection.
+
+ This is usually a class or function name.
+ """
+ text = htmlescape(expandtabs(text))
+ result = []
+ result.append("""""")
+ result.append("""""" % bgcolor)
+ result.append("""%s""" % text)
+ result.append(""" |
""")
+ result.append('')
+
+ return join(result, '\n')
+
+
+def makeHtmlInlineImage(text):
+ """Create HTML code for an inline image.
+ """
+
+ return """""" % (text, text)
+
+
+####################################################################
+#
+# Core "standard" docpy classes
+#
+####################################################################
+
+class PackageSkeleton0:
+ """A class collecting 'interesting' information about a package."""
+ pass # Not yet!
+
+
+class ModuleSkeleton0:
+ """A class collecting 'interesting' information about a module."""
+
+ def __init__(self):
+ # This is an ad-hoc, somewhat questionable 'data structure',
+ # but for the time being it serves its purpose and is fairly
+ # self-contained.
+ self.module = {}
+ self.functions = {}
+ self.classes = {}
+
+
+ # Might need more like this, later.
+ def getModuleName(self):
+ """Return the name of the module being treated."""
+
+ return self.module['name']
+
+
+ # These inspect methods all rely on the inspect module.
+ def inspect(self, object):
+ """Collect information about a given object."""
+
+ self.moduleSpace = object
+
+ # Very non-OO, left for later...
+ if inspect.ismodule(object):
+ self._inspectModule(object)
+ elif inspect.isclass(object):
+ self._inspectClass(object)
+ elif inspect.ismethod(object):
+ self._inspectMethod(object)
+ elif inspect.isfunction(object):
+ self._inspectFunction(object)
+ elif inspect.isbuiltin(object):
+ self._inspectBuiltin(object)
+ else:
+ msg = "Don't know how to document this kind of object."
+ raise TypeError, msg
+
+
+ def _inspectModule(self, object):
+ """Collect information about a given module object."""
+ name = object.__name__
+
+ self.module['name'] = name
+ if hasattr(object, '__version__'):
+ self.module['version'] = object.__version__
+
+ cadr = lambda list: list[1]
+ modules = map(cadr, inspect.getmembers(object, inspect.ismodule))
+
+ classes, cdict = [], {}
+ for key, value in inspect.getmembers(object, inspect.isclass):
+ if (inspect.getmodule(value) or object) is object:
+ classes.append(value)
+ cdict[key] = cdict[value] = '#' + key
+
+ functions, fdict = [], {}
+ for key, value in inspect.getmembers(object, inspect.isroutine):
+ #if inspect.isbuiltin(value) or inspect.getmodule(value) is object:
+ functions.append(value)
+ fdict[key] = '#-' + key
+ if inspect.isfunction(value): fdict[value] = fdict[key]
+
+ for c in classes:
+ for base in c.__bases__:
+ key, modname = base.__name__, base.__module__
+ if modname != name and sys.modules.has_key(modname):
+ module = sys.modules[modname]
+ if hasattr(module, key) and getattr(module, key) is base:
+ if not cdict.has_key(key):
+ cdict[key] = cdict[base] = modname + '.txt#' + key
+
+## doc = getdoc(object) or 'No doc string.'
+ doc = getdoc(object)
+ self.module['doc'] = doc
+
+ if modules:
+ self.module['importedModules'] = map(lambda m:m.__name__, modules)
+
+ if classes:
+ for item in classes:
+ self._inspectClass(item, fdict, cdict)
+
+ if functions:
+ for item in functions:
+ self._inspectFunction(item, fdict, cdict)
+
+
+ def _inspectClass(self, object, functions={}, classes={}):
+ """Collect information about a given class object."""
+
+ name = object.__name__
+ bases = object.__bases__
+ results = []
+
+ if bases:
+ parents = []
+ for base in bases:
+ parents.append(base)
+
+ self.classes[name] = {}
+ if bases:
+ self.classes[name]['bases'] = parents
+
+ methods, mdict = [], {}
+ for key, value in inspect.getmembers(object, inspect.ismethod):
+ methods.append(value)
+ mdict[key] = mdict[value] = '#' + name + '-' + key
+
+ if methods:
+ if not self.classes[name].has_key('methods'):
+ self.classes[name]['methods'] = {}
+ for item in methods:
+ self._inspectMethod(item, functions, classes, mdict, name)
+
+## doc = getdoc(object) or 'No doc string.'
+ doc = getdoc(object)
+ self.classes[name]['doc'] = doc
+
+
+ def _inspectMethod(self, object, functions={}, classes={}, methods={}, clname=''):
+ """Collect information about a given method object."""
+
+ self._inspectFunction(object.im_func, functions, classes, methods, clname)
+
+
+ def _inspectFunction(self, object, functions={}, classes={}, methods={}, clname=''):
+ """Collect information about a given function object."""
+ try:
+ args, varargs, varkw, defaults = inspect.getargspec(object)
+ argspec = inspect.formatargspec(
+ args, varargs, varkw, defaults,
+ defaultformat=defaultformat)
+ except TypeError:
+ argspec = '( ... )'
+
+## doc = getdoc(object) or 'No doc string.'
+ doc = getdoc(object)
+
+ if object.__name__ == '':
+ decl = [' lambda ', argspec[1:-1]]
+ # print ' %s' % decl
+ # Do something with lambda functions as well...
+ # ...
+ else:
+ decl = object.__name__
+ if not clname:
+ self.functions[object.__name__] = {'signature':argspec, 'doc':doc}
+ else:
+ theMethods = self.classes[clname]['methods']
+ if not theMethods.has_key(object.__name__):
+ theMethods[object.__name__] = {}
+
+ theMethod = theMethods[object.__name__]
+ theMethod['signature'] = argspec
+ theMethod['doc'] = doc
+
+
+ def _inspectBuiltin(self, object):
+ """Collect information about a given built-in."""
+
+ print object.__name__ + '( ... )'
+
+
+ def walk(self, formatter):
+ """Call event methods in a visiting formatter."""
+
+ s = self
+ f = formatter
+
+ # The order is fixed, but could be made flexible
+ # with one more template method...
+
+ # Module
+ modName = s.module['name']
+ modDoc = s.module['doc']
+ imported = s.module.get('importedModules', [])
+ imported.sort()
+ # f.indentLevel = f.indentLevel + 1
+ f.beginModule(modName, modDoc, imported)
+
+ # Classes
+ f.indentLevel = f.indentLevel + 1
+ f.beginClasses(s.classes.keys())
+ items = s.classes.items()
+ items.sort()
+ for k, v in items:
+ cDoc = s.classes[k]['doc']
+ bases = s.classes[k].get('bases', [])
+ f.indentLevel = f.indentLevel + 1
+ f.beginClass(k, cDoc, bases)
+
+ # This if should move out of this method.
+ if not s.classes[k].has_key('methods'):
+ s.classes[k]['methods'] = {}
+
+ # Methods
+ #f.indentLevel = f.indentLevel + 1
+ f.beginMethods(s.classes[k]['methods'].keys())
+ items = s.classes[k]['methods'].items()
+ items.sort()
+ for m, v in items:
+ mDoc = v['doc']
+ sig = v['signature']
+ f.indentLevel = f.indentLevel + 1
+ f.beginMethod(m, mDoc, sig)
+ f.indentLevel = f.indentLevel - 1
+ f.endMethod(m, mDoc, sig)
+
+ #f.indentLevel = f.indentLevel - 1
+ f.endMethods(s.classes[k]['methods'].keys())
+
+ f.indentLevel = f.indentLevel - 1
+ f.endClass(k, cDoc, bases)
+
+ # And what about attributes?!
+
+ f.indentLevel = f.indentLevel - 1
+ f.endClasses(s.classes.keys())
+
+ # Functions
+ f.indentLevel = f.indentLevel + 1
+ f.beginFunctions(s.functions.keys())
+ items = s.functions.items()
+ items.sort()
+ for k, v in items:
+ doc = v['doc']
+ sig = v['signature']
+ f.indentLevel = f.indentLevel + 1
+ f.beginFunction(k, doc, sig)
+ f.indentLevel = f.indentLevel - 1
+ f.endFunction(k, doc, sig)
+ f.indentLevel = f.indentLevel - 1
+ f.endFunctions(s.functions.keys())
+
+ #f.indentLevel = f.indentLevel - 1
+ f.endModule(modName, modDoc, imported)
+
+ # Constants?!
+
+
+####################################################################
+#
+# Core "standard" docpy document builders
+#
+####################################################################
+
+class DocBuilder0:
+ """An abstract class to document the skeleton of a Python module.
+
+ Instances take a skeleton instance s and call their s.walk()
+ method. The skeleton, in turn, will walk over its tree structure
+ while generating events and calling equivalent methods from a
+ specific interface (begin/end methods).
+ """
+
+ fileSuffix = None
+
+ def __init__(self, skeleton=None):
+ self.skeleton = skeleton
+ self.packageName = None
+ self.indentLevel = 0
+
+
+ def write(self, skeleton=None):
+ if skeleton:
+ self.skeleton = skeleton
+ self.skeleton.walk(self)
+
+
+ # Event-method API, called by associated skeleton instances.
+ # In fact, these should raise a NotImplementedError, but for now we
+ # just don't do anything here.
+
+ # The following four methods are *not* called by skeletons!
+ def begin(self, name='', typ=''): pass
+ def end(self): pass
+
+ # Methods for packaging should move into a future PackageSkeleton...
+ def beginPackage(self, name):
+ self.packageName = name
+
+ def endPackage(self, name):
+ pass
+
+ # Only this subset is really called by associated skeleton instances.
+
+ def beginModule(self, name, doc, imported): pass
+ def endModule(self, name, doc, imported): pass
+
+ def beginClasses(self, names): pass
+ def endClasses(self, names): pass
+
+ def beginClass(self, name, doc, bases): pass
+ def endClass(self, name, doc, bases): pass
+
+ def beginMethods(self, names): pass
+ def endMethods(self, names): pass
+
+ def beginMethod(self, name, doc, sig): pass
+ def endMethod(self, name, doc, sig): pass
+
+ def beginFunctions(self, names): pass
+ def endFunctions(self, names): pass
+
+ def beginFunction(self, name, doc, sig): pass
+ def endFunction(self, name, doc, sig): pass
+
+
+class AsciiDocBuilder0(DocBuilder0):
+ """Document the skeleton of a Python module in ASCII format.
+
+ The output will be an ASCII file with nested lines representing
+ the hiearchical module structure.
+
+ Currently, no doc strings are listed.
+ """
+
+ fileSuffix = '.txt'
+ outLines = []
+ indentLabel = ' '
+
+ def end(self):
+ # This if should move into DocBuilder0...
+ if self.packageName:
+ self.outPath = self.packageName + self.fileSuffix
+ elif self.skeleton:
+ self.outPath = self.skeleton.getModuleName() + self.fileSuffix
+ else:
+ self.outPath = ''
+
+ if self.outPath:
+ file = open(self.outPath, 'w')
+ for line in self.outLines:
+ file.write(line + '\n')
+ file.close()
+
+
+ def beginPackage(self, name):
+ DocBuilder0.beginPackage(self, name)
+ lev, label = self.indentLevel, self.indentLabel
+ self.outLines.append('%sPackage: %s' % (lev*label, name))
+ self.outLines.append('')
+
+
+ def beginModule(self, name, doc, imported):
+ append = self.outLines.append
+ lev, label = self.indentLevel, self.indentLabel
+ self.outLines.append('%sModule: %s' % (lev*label, name))
+## self.outLines.append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
+ append('')
+
+ if imported:
+ self.outLines.append('%sImported' % ((lev+1)*label))
+ append('')
+ for m in imported:
+ self.outLines.append('%s%s' % ((lev+2)*label, m))
+ append('')
+
+
+ def beginClasses(self, names):
+ if names:
+ lev, label = self.indentLevel, self.indentLabel
+ self.outLines.append('%sClasses' % (lev*label))
+ self.outLines.append('')
+
+
+ def beginClass(self, name, doc, bases):
+ append = self.outLines.append
+ lev, label = self.indentLevel, self.indentLabel
+
+ if bases:
+ bases = map(lambda b:b.__name__, bases) # hack
+ append('%s%s(%s)' % (lev*label, name, join(bases, ', ')))
+ else:
+ append('%s%s' % (lev*label, name))
+ return
+
+## append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
+ self.outLines.append('')
+
+
+ def endClass(self, name, doc, bases):
+ self.outLines.append('')
+
+
+ def beginMethod(self, name, doc, sig):
+ append = self.outLines.append
+ lev, label = self.indentLevel, self.indentLabel
+ append('%s%s%s' % (lev*label, name, sig))
+## append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
+## append('')
+
+
+ def beginFunctions(self, names):
+ if names:
+ lev, label = self.indentLevel, self.indentLabel
+ self.outLines.append('%sFunctions' % (lev*label))
+ self.outLines.append('')
+
+
+ def endFunctions(self, names):
+ self.outLines.append('')
+
+
+ def beginFunction(self, name, doc, sig):
+ append = self.outLines.append
+ lev, label = self.indentLevel, self.indentLabel
+ self.outLines.append('%s%s%s' % (lev*label, name, sig))
+## append('%s%s' % ((lev+1)*label, reduceDocStringLength(doc)))
+## append('')
+
+
+class HtmlDocBuilder0(DocBuilder0):
+ "A class to write the skeleton of a Python source in HTML format."
+
+ fileSuffix = '.html'
+ outLines = []
+
+ def begin(self, name='', typ=''):
+ self.outLines.append("""""")
+ self.outLines.append("""""")
+
+
+ def end(self):
+ if self.packageName:
+ self.outPath = self.packageName + self.fileSuffix
+ elif self.skeleton:
+ self.outPath = self.skeleton.getModuleName() + self.fileSuffix
+ else:
+ self.outPath = ''
+
+ if self.outPath:
+ file = open(self.outPath, 'w')
+ self.outLines.append('