[Checkins] SVN: grok/branches/ulif-reference/doc/reference/ Add tool to introspect/format the grok API. Update reference-README in ulif-reference-branch.

Uli Fouquet uli at mediamorphose.org
Thu May 31 10:19:57 EDT 2007


Log message for revision 76046:
  Add tool to introspect/format the grok API. Update reference-README in ulif-reference-branch.

Changed:
  U   grok/branches/ulif-reference/doc/reference/README.txt
  A   grok/branches/ulif-reference/doc/reference/mkgrokref.py

-=-
Modified: grok/branches/ulif-reference/doc/reference/README.txt
===================================================================
--- grok/branches/ulif-reference/doc/reference/README.txt	2007-05-31 06:30:03 UTC (rev 76045)
+++ grok/branches/ulif-reference/doc/reference/README.txt	2007-05-31 14:19:55 UTC (rev 76046)
@@ -41,3 +41,34 @@
 This script will be located in::
 
   /usr/lib/python2.4/doc/tools/mkhowto
+
+
+Generating the grok reference from grok source code
+---------------------------------------------------
+
+.. warning:: This is work-in-progress and may lead to disappointing
+             results.
+
+The script mkgrokref.py is able to generate the sources for grok
+reference from the grok source code. It must be run with the
+appropriate PYTHONPATH set (i.e.: including the grok package to scan,
+all packages required by grok, namely the zope 3 packages, and the
+python standard packages). See the header of the script for further
+documentation. There are currently no commandline options supported by
+mkgrokref.py.
+
+To get a very plain reference::
+
+  $ python2.4 mkgrokref.py > myreference.txt
+
+This will produce a ReST formatted text file. Go on with::
+
+  $ rst2latex myreference.txt myreference.tex
+
+Transform the ReStructucturedText to LaTeX. Now we got a LaTeX source,
+which can be further processed as described above. For example::
+
+  $ mkhowto --pdf myreference.tex
+
+The rst2latex script is part of the python docutils package.
+

Added: grok/branches/ulif-reference/doc/reference/mkgrokref.py
===================================================================
--- grok/branches/ulif-reference/doc/reference/mkgrokref.py	                        (rev 0)
+++ grok/branches/ulif-reference/doc/reference/mkgrokref.py	2007-05-31 14:19:55 UTC (rev 76046)
@@ -0,0 +1,374 @@
+"""Create grok reference introspecting the grok package.
+
+This script is able to generate reference docs for grok. It consists
+of three classes:
+
+ - DocMetaData:
+
+    Pure data collector to collect als configuration drivers.
+
+ - APIParser:
+
+    The introspector. Examines the grok package and delivers the
+    elements found as a nested dictionary.
+
+ - APIFormatter:
+
+    Formats api-dicts as generated by APIParser in a manner suitable
+    for further processing. Supported formats:
+    
+      - LaTeX
+
+      - ReStructured Text
+
+      - Plain text
+
+The generated output must be processed further to get end-user
+documents, for example by using rst2latex or mkhowto.
+
+To make this script working, the PYTHONPATH environment variable must
+be set properly. It must include the grok sources that should be
+documented.
+
+    
+"""
+
+import grok
+import types
+from zope.app.apidoc import utilities, component, interface
+from zope.app.apidoc.classregistry import ClassRegistry
+from grok.interfaces import *
+
+class DocMetaData:
+    """Meta data for API-doc generation.
+
+    """
+
+    complete = True
+    """Generate docs with title and header or only the chapters."""
+
+    pedantic = False
+    """Generate warnings if missing docstrings are found"""
+    
+    latex_optimized = False
+    """Generate output more suitable for LaTeX """
+
+    sections={"Components":IGrokBaseClasses,
+              "Grok Errors":IGrokErrors,
+              "Directives":IGrokDirectives,
+              "Decorators":IGrokDecorators,
+              "Events":IGrokEvents,
+              "View":IGrokView,
+              "Form":IGrokForm,
+              "Application":IApplication
+              }
+    """Sections we want to see in the reference."""
+    
+    doc_data = { 
+        'title' : "grok reference",
+        'authoraddress' : ("The grok team","<grok-dev at zope.org>"),
+        'release' : "unreleased",
+        'motto' : """``Grok means to understand so thoroughly that the observer becomes a part of the observed -- merge, blend, intermarry, lose identity in group experience. It means almost everything that we mean by religion, philosophy, and science -- it means as little to us (because we are from Earth) as color means to a blind man.'' - Robert A. Heinlein, Stranger in a Strange Land """,
+        'abstract' : """This is the grok reference documentation. It is organized by the Python artefacts that implement the concepts.
+        
+        Grok makes Zope 3 concepts more accessible for application developers. This reference is not intended as introductory material for those concepts. Please refer to the original Zope 3 documentation and the grok tutorial for introductory material."""}
+    """Meta data to incorporate into the reference."""
+
+
+class APIParser:
+    """Create a nested dict of API elements.
+    """
+
+    meta = DocMetaData()
+    
+    def getAttrDocAsDict(self, cls, attrname ):
+        """Examine attribute.
+        """
+        result = {'name':attrname,
+                  'type':None,
+                  'sig':None,
+                  'doc':None}
+        obj = getattr(cls,attrname)
+        typeofobj = type(obj)
+        sig = ""
+        if isinstance(obj, (types.FunctionType, types.MethodType)):
+            sig = utilities.getFunctionSignature(obj)
+        result['type'] = typeofobj
+        result['sig'] = sig
+            
+        doc = ""
+        if (not hasattr(obj, '__doc__')) or (obj.__doc__ is None):
+            if self.meta.pedantic:
+                doc = "\nXXX docstring documentation of attribute missing.\n"
+        else:
+            doc = utilities.dedentString( obj.__doc__ )
+        #doc = replaceEvilChars(doc,self.meta.latex_optimized)
+        result = {'name':attrname,
+                  'type':typeofobj,
+                  'sig':sig,
+                  'doc':doc }
+        return result
+                
+    def getClassDocAsDict(self, clsname):
+        """Examine class of classname.
+        """
+        result = {"name":"grok."+clsname}
+                    
+        if clsname[0] == "_":
+            return None
+                    
+        if clsname in grok.__dict__.keys():
+            cls = grok.__dict__[clsname]
+            reg[clsname] = cls
+        else:
+            # Not a real class, but it might help to keep
+            # informed...
+            return None
+
+        cls_path = "Could not get path"
+        try:
+            cls_path = utilities.getPythonPath(cls)
+        except:
+            pass
+        result['path'] = cls_path
+    
+        doc = ""
+        if not hasattr(cls, '__doc__') or cls.__doc__ is None:
+            if self.meta.pedantic:
+                doc = "\nXXX docstring documentation of class missing.\n"
+        else:
+            doc = utilities.dedentString( cls.__doc__ )
+
+        result['doc'] = doc
+
+        apidoc_parse = ""
+        if hasattr( cls, 'factory'):
+            apidoc_parse = component.getAdapterInfoDictionary(cls)
+        if hasattr( cls, 'component'):
+            apidoc_parse = component.getUtilityInfoDictionary(cls)
+    
+        result['elems'] = []
+        elemlist = utilities.getPublicAttributes(cls)
+        result['elems'] = [self.getAttrDocAsDict(cls,x) for x in elemlist]
+        result['elems'].sort()
+        return result
+        
+
+    def getAPIDict(self,groups_dict):
+        """Get a nested directory containing the grok API
+
+        """
+        result_groups = []
+        for (key,group) in groups_dict.items():
+            result = {'group':key}
+            classes = [x for x in group]
+            classes.sort()
+            result['classes'] = []
+            for clsname in classes:
+                class_api = self.getClassDocAsDict(clsname)
+                if class_api is not None:
+                    result['classes'].append( class_api )
+            result_groups.append( result )
+        return result_groups
+
+
+class APIFormatter:
+    """Helpers to generate readable API doc in various formats.
+    """
+
+    meta = DocMetaData()
+    api_data = None
+
+    def indentText(self,text,num):
+        """Indent long strings.
+
+        XXX broken
+        """
+        result = ""
+        for line in text.split("\n"):
+            result += "%s%s\n" % (" "*num,line)
+        return result
+
+    def replaceEvilChars(self,text):
+        """ Replace evil characters.
+        """
+        replace_tuples = [('$','\$')]
+        for subst in replace_tuples:
+            text = text.replace( subst[0], subst[1] )
+        if self.meta.latex_optimized:
+            replace_tuples = ( (
+                ("\n", "\n\n"),
+                ('\\$\\rightarrow\\$','$\\rightarrow$'),
+                ('->', '$\\rightarrow$'),
+                ('\$>\$', '>'),('\$<\$', '<'),
+                ('>','$>$'), ('<','$<$')) )
+            for subst in replace_tuples:
+                text = text.replace( subst[0], subst[1] )
+        return text
+
+
+    def prepareDocString(self,doc,num):
+        """Indent text lines and split headline from body.
+        """
+        doc = self.indentText(doc,num)
+        doc = self.replaceEvilChars(doc)
+        lines = doc.split('\n')
+        head = doc
+        body = doc
+        if len(lines):
+            head = lines[0]
+            body = '\n'.join(lines[1:])
+        if len(head) > 1:
+            head = " -- %s" % head
+        return (head,body)
+
+    def getTexOutput(self,api_dict):
+        """Transfom api_dict to TeX compatible output.
+        """
+        result = ""
+        if self.meta.complete:
+            result = '''
+\documentclass{manual}
+\RequirePackage[latin9]{inputenc}
+\usepackage{graphicx}
+\\title{%s}
+\\authoraddress{
+  %s
+  Email: %s
+}
+\\date{\\today}
+\\release{%s}
+\makeindex
+\\begin{document}
+\maketitle
+
+%s
+
+\\begin{abstract}
+
+%s
+
+\end{abstract}
+
+\\tableofcontents
+
+\include{core}
+
+''' % (self.meta.doc_data['title'],
+       self.meta.doc_data['authoraddress'][0],
+       self.replaceEvilChars(self.meta.doc_data['authoraddress'][1]),
+       self.meta.doc_data['release'],
+       self.meta.doc_data['motto'],
+       self.meta.doc_data['abstract'],
+       )
+            for group in api_dict:
+                result += "\chapter{%s}\n\n" % group['group']
+                for cls in group['classes']:
+                    (head,body) = self.prepareDocString(
+                        cls['doc'], 6)
+                    body = "\ "+body
+                    result += "  \section{\class{%s%s}}\n\n" % (
+                        cls['name'],head)
+                    result += "    \\begin{classdesc*}{%s%s}\n\n" % (
+                        cls['name'],head)
+                    result += "%s\n"%body
+                    for elem in cls['elems']:
+                        result += "      \\begin{memberdesc}{%s%s}\n"% (
+                            elem['name'], elem['sig'] )
+                        result += self.replaceEvilChars(elem['doc']) + "\n\n"
+                        result += "      \end{memberdesc}\n\n"
+                    result += "    \end{classdesc*}\n\n"
+
+        if self.meta.complete:
+            result += "\n\end{document}"
+        return result
+
+
+    def getPlainOutput(self,api_dict):
+        """Grok API as plain text
+        
+        Returns a long string representing the api_dict given.
+        """
+        result = ""
+        for section in api_dict:
+            print section['group']
+            for cls in section['classes']:
+                print "  " + cls['name']
+                doc = self.indentText( cls['doc'], 6)
+                print "%s" % doc
+                for elem in cls['elems']:
+                    print "    " + elem['name'] + elem['sig']
+                    doc = self.indentText( elem['doc'], 10)
+                    print doc
+        return result
+
+    def getReSTOutput(self,api_dict):
+        """Grok API as ReStructuredText.
+        
+        Returns a long string representing the api_dict given.
+        """
+        result = ""
+        doc_data = self.meta.doc_data
+
+        if self.meta.complete:
+            # Generate header...
+            result += "\n%s %s\n\n%s\n\n%s\n\n%s\n%s\n%s\n\n" % (
+                ".. title ", doc_data['title'],
+                ".. sectnum::",
+                ".. |date| date::",
+                "="*len(doc_data['title']),
+                doc_data['title'],
+                "="*len(doc_data['title']))
+
+            # Add table of contents...
+            result += "%s\n\n" % (
+                ".. contents:: Table of Contents",
+                )
+        
+        for section in api_dict:
+            result += "%s\n%s\n\n" % (
+                section['group'],
+                "=" * len(section['group']))
+            for cls in section['classes']:
+                result += "\n%s\n%s\n\n" % (
+                    cls['name'],
+                    "-" * len(cls['name']))
+                doc = self.indentText( cls['doc'], 0)
+                result += "%s\n\nMethods and Attributes:\n\n" % (doc,)
+                for elem in cls['elems']:
+                    result += "   - %s%s:\n\n" %( elem['name'], elem['sig'] )
+                    doc = self.indentText( elem['doc'], 10)
+                    result += "%s\n" % (doc,)
+        return result
+
+# Fill class registry with all grok elements...
+reg = ClassRegistry()
+for name in grok.__dict__.keys():
+    if name[0] == "_":
+        continue
+    reg[name] = grok.__dict__[name]
+
+
+if __name__ == "__main__":
+    meta = DocMetaData()
+    parser = APIParser()
+    parser.meta = meta
+    api_dict = parser.getAPIDict(meta.sections)
+
+
+    #for section in result:
+    #    print section['group']
+    #    for cls in section['classes']:
+    #        print "  " + cls['name']
+    #        for elem in cls['elems']:
+    #            print "    " + elem['name'] + elem['sig']
+
+    formatter = APIFormatter()
+    formatter.meta = meta
+    tex_output = formatter.getTexOutput( api_dict )
+    #print tex_output
+
+    #formatter.getPlainOutput( api_dict )
+    
+    rest_output = formatter.getReSTOutput( api_dict )
+    print rest_output



More information about the Checkins mailing list