[Zope3-checkins] CVS: Zope3/src/zope/configuration - docutils.py:1.1 stxdocs.py:1.1 config.py:1.14

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Jan 22 18:53:16 EST 2004


Update of /cvs-repository/Zope3/src/zope/configuration
In directory cvs.zope.org:/tmp/cvs-serv6835/zope/configuration

Modified Files:
	config.py 
Added Files:
	docutils.py stxdocs.py 
Log Message:
Implemented new ZCML doc generator.

I modified config.py so that some meta-data information is recorded that is
documentation generation friendly. I added a method 'document()' to the 
Configuration machine to support that. The 'document()' method actually 
collects information that was lost before.

stxdocs.py creates the documentation in STX format. Each directive and its
subdirectives go into one file. Each namespace is a directory.

docutils.py contains some helper functions that might be useful for other
doc generators (like RNG XML Schema and LaTeX).



=== Added File Zope3/src/zope/configuration/docutils.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Helper Utility to wrap a text to a set width of characters

$Id: docutils.py,v 1.1 2004/01/22 23:53:15 srichter Exp $
"""
import re

para_sep = re.compile('\n{2,}')
whitespace=re.compile('[ \t\n\r]+')

def wrap(text, width=78, indent=0):
    """ """
    paras = para_sep.split(text.strip())

    new_paras = []
    for par in paras:
	words= filter(None, whitespace.split(par))
        
        lines = []
        line = []
        length = indent
        for word in words:
            if length + len(word) + 1 <= width:
                line.append(word)
                length += len(word) + 1
            else:
                lines.append(' '*indent + ' '.join(line))
                line = []
                length = indent

        lines.append(' '*indent + ' '.join(line))
        
        new_paras.append('\n'.join(lines))

    return '\n\n'.join(new_paras) + '\n\n'


def makeDocStructures(context):
    """ """
    namespaces = {}
    subdirs = {}
    for (namespace, name), schema, usedIn, info, parent in context._docRegistry:
        if not parent:
            ns_entry = namespaces.setdefault(namespace, {})
            ns_entry[name] = (schema, info)
        else:
            sd_entry = subdirs.setdefault((parent.namespace, parent.name), [])
            sd_entry.append((namespace, name, schema, info))
    return namespaces, subdirs    


=== Added File Zope3/src/zope/configuration/stxdocs.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""STX Configuration Documentation Renderer

Usage: stxdocs.py [options]
Options:
    -h / --help
        Print this message and exit.

    -f <path>
        Specifies the root ZCML meta directives file, relative to the current
        location. All included files will be considered as well

    -o <dir>
        Specifies a directory, relative to the current location in which the
        documentation is stored. Note that this tool will create
        sub-directories with files in them. 

$Id: stxdocs.py,v 1.1 2004/01/22 23:53:15 srichter Exp $
"""
import sys, os, getopt
import zope
from zope.schema import getFieldsInOrder 
from zope.configuration import config, xmlconfig
from zope.configuration.docutils import wrap, makeDocStructures

def usage(code, msg=''):
    # Python 2.1 required
    print >> sys.stderr, __doc__
    if msg:
        print >> sys.stderr, msg
    sys.exit(code)

def _directiveDocs(name, schema, info, indent_offset=0):
    """Generate the documentation for one directive."""

    # Write out the name of the directive
    text  = ' '*indent_offset
    text +=  '%s\n\n' %name

    # Specify the file and location it has been declared
    if isinstance(info, xmlconfig.ParserInfo):
        # We do not want to specify the whole path; starting at the 'zope'
        # package is enough.
        base_dir = os.path.split(zope.__file__)[0][:-4]
        file = info.file.replace(base_dir, '')

        info_text = 'File %s, lines %i - %i.' %(file, info.line, info.eline)
        text += wrap(info_text, 78, indent_offset+2)

    elif isinstance(info, (str, unicode)) and info:
        text += wrap(info, 78, indent_offset+2)

    # Use the schema documentation string as main documentation text for the
    # directive.
    text += wrap(schema.getDoc(), 78, indent_offset+2)
    text += ' '*indent_offset + '  Attributes\n\n'

    # Create directive attribute documentation
    for name, field in getFieldsInOrder(schema):
        name = name.strip('_')
        if field.required:
            opt = 'required'
        else:
            opt = 'optional, default=%s' %field.default.__repr__()
        text += ' '*indent_offset
        text += '    %s -- %s (%s)\n\n' %(name, field.__class__.__name__, opt)

        text += wrap(field.title, 78, indent_offset+6)
        text += wrap(field.description, 78, indent_offset+6)

    return text

def _subDirectiveDocs(subdirs, namespace, name):
    """Appends a list of sub-directives and their full specification."""
    if subdirs.has_key((namespace, name)):
        text = '\n  Subdirectives\n\n'
        sub_dirs = []
        # Simply walk through all sub-directives here.
        for sd_ns, sd_name, sd_schema, sd_info in subdirs[(namespace, name)]:
            sub_dirs.append(_directiveDocs(sd_name, sd_schema, sd_info, 4))

        return text + '\n\n'.join(sub_dirs)
    return ''

def makedocs(target_dir, zcml_file):
    """Generate the documentation tree.

    All we need for this is a starting ZCML file and a directory in which to
    put the documentation.
    """
    context = xmlconfig.file(zcml_file, execute=False)
    namespaces, subdirs = makeDocStructures(context)

    for namespace, directives in namespaces.items():
        ns_dir = os.path.join(target_dir, namespace.split('/')[-1])
        # Create a directory for the namespace, if necessary
        if not os.path.exists(ns_dir):
            os.mkdir(ns_dir)

        # Create a file for each directive
        for name, (schema, info) in directives.items():
            dir_file = os.path.join(ns_dir, name+'.stx')
            text = _directiveDocs(name, schema, info)
            text += _subDirectiveDocs(subdirs, namespace, name)
            open(dir_file, 'w').write(text)

def _makeabs(path):
    """Make an absolute path from the possibly relative path."""
    if not path == os.path.abspath(path):
        cwd = os.getcwd()
        # This is for symlinks.
        if os.environ.has_key('PWD'):
            cwd = os.environ['PWD']
        path = os.path.normpath(os.path.join(cwd, path))    
    return path

def main(argv=sys.argv):
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            'h:f:o:',
            ['help'])
    except getopt.error, msg:
        usage(1, msg)

    zcml_file = None
    output_dir = None
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(0)
        elif opt in ('-o', ):
            output_dir = arg
        elif opt in ('-f', ):
            zcml_file = _makeabs(arg)
            if not os.path.exists(zcml_file):
                usage(1, 'The specified zcml file does not exist.')

    if zcml_file is None or output_dir is None:
        usage(0, "Both, the '-f' and '-o' option are required")

    # Generate the docs
    makedocs(output_dir, zcml_file)

if __name__ == '__main__':
    main()


=== Zope3/src/zope/configuration/config.py 1.13 => 1.14 ===
--- Zope3/src/zope/configuration/config.py:1.13	Wed Dec 17 03:06:10 2003
+++ Zope3/src/zope/configuration/config.py	Thu Jan 22 18:53:15 2004
@@ -310,11 +310,32 @@
     >>> r.register(IConfigurationContext, 'yyy', f)
     >>> r.factory(c, ('http://www.zope.com','yyy')) is f
     1
+
+    Test the documentation feature:
+
+    >>> r._docRegistry
+    []
+    >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, 'inf', None)
+    >>> r._docRegistry[0][0] == ('ns', 'dir')
+    1
+    >>> r._docRegistry[0][1] is IFullInfo
+    1
+    >>> r._docRegistry[0][2] is IConfigurationContext
+    1
+    >>> r._docRegistry[0][3] == 'inf'
+    1
+    >>> r._docRegistry[0][4] is None
+    1
+    >>> r.document('all-dir', None, None, None, None)
+    >>> r._docRegistry[1][0]
+    ('', 'all-dir')
     """
 
 
     def __init__(self):
         self._registry = {}
+        # Stores tuples of form: (namespace, name), schema, usedIn, info, parent
+        self._docRegistry = []
 
     def register(self, interface, name, factory):
         r = self._registry.get(name)
@@ -324,6 +345,11 @@
 
         r.register(interface, Interface, factory)
 
+    def document(self, name, schema, usedIn, info, parent=None):
+        if isinstance(name, (str, unicode)):
+            name = ('', name)
+        self._docRegistry.append((name, schema, usedIn, info, parent))
+
     def factory(self, context, name):
         r = self._registry.get(name)
         if r is None:
@@ -484,7 +510,7 @@
 class IStackItem(Interface):
     """Configuration machine stack items
 
-    Stack items are created when a directive if being processed.
+    Stack items are created when a directive is being processed.
 
     A stack item is created for each directive use.
     """
@@ -841,7 +867,7 @@
 class GroupingContextDecorator(ConfigurationContext):
     """Helper mix-in class for building grouping directives
 
-    See the discussion (and test) id GroupingStackItem.
+    See the discussion (and test) in GroupingStackItem.
     """
 
     implements(IConfigurationContext, IGroupingContext)
@@ -982,6 +1008,7 @@
         return SimpleStackItem(context, handler, info, schema, data)
 
     context.register(usedIn, name, factory)
+    context.document(name, schema, usedIn, context.info)
 
 def defineGroupingDirective(context, name, schema, handler,
                             namespace='', usedIn=IConfigurationContext):
@@ -1040,6 +1067,7 @@
         return GroupingStackItem(newcontext)
 
     context.register(usedIn, name, factory)
+    context.document(name, schema, usedIn, context.info)
 
 
 class IComplexDirectiveContext(IFullInfo, IConfigurationContext):
@@ -1059,8 +1087,12 @@
             return ComplexStackItem(self, context, data, info)
 
         self.register(self.usedIn, (self.namespace, self.name), factory)
+        self.document((self.namespace, self.name), self.schema, self.usedIn,
+                      self.info)
 
 def subdirective(context, name, schema):
+    context.document((context.namespace, name), schema, context.usedIn,
+                     context.info, context.context)
     context.context[name] = schema, context.info
 
 
@@ -1354,11 +1386,15 @@
     # Define the directive (simple directive) directive by calling it's
     # handler directly
 
+    info = 'Manually registered in zope/configuration/config.py'
+
+    context.info = info
     defineSimpleDirective(
         context,
         namespace=metans, name='directive',
         schema=IStandaloneDirectiveInfo,
         handler=defineSimpleDirective)
+    context.info = ''
 
     # OK, now that we have that, we can use the machine to define the
     # other directives. This isn't the easiest way to proceed, but it lets
@@ -1366,6 +1402,7 @@
 
     # Standalone groupingDirective
     context((metans, 'directive'),
+            info,
             name='groupingDirective',
             namespace=metans,
             handler="zope.configuration.config.defineGroupingDirective",
@@ -1374,6 +1411,7 @@
 
     # Now we can use the grouping directive to define the directives directive
     context((metans, 'groupingDirective'),
+            info,
             name='directives',
             namespace=metans,
             handler="zope.configuration.config.DirectivesHandler",
@@ -1382,6 +1420,7 @@
 
     # directive and groupingDirective inside directives
     context((metans, 'directive'),
+            info,
             name='directive',
             namespace=metans,
             usedIn="zope.configuration.config.IDirectivesContext",
@@ -1389,6 +1428,7 @@
             schema="zope.configuration.config.IFullInfo"
             )
     context((metans, 'directive'),
+            info,
             name='groupingDirective',
             namespace=metans,
             usedIn="zope.configuration.config.IDirectivesContext",
@@ -1399,12 +1439,14 @@
     # Setup complex directive directive, both standalone, and in
     # directives directive
     context((metans, 'groupingDirective'),
+            info,
             name='complexDirective',
             namespace=metans,
             handler="zope.configuration.config.ComplexDirectiveDefinition",
             schema="zope.configuration.config.IStandaloneDirectiveInfo"
             )
     context((metans, 'groupingDirective'),
+            info,
             name='complexDirective',
             namespace=metans,
             usedIn="zope.configuration.config.IDirectivesContext",
@@ -1414,6 +1456,7 @@
 
     # Finally, setup subdirective directive
     context((metans, 'directive'),
+            info,
             name='subdirective',
             namespace=metans,
             usedIn="zope.configuration.config.IComplexDirectiveContext",




More information about the Zope3-Checkins mailing list