[Zope3-checkins] SVN: Zope3/trunk/ Finished my work on the apidoc tool. In this last milestone I rewrote the

Stephan Richter srichter at cosmos.phy.tufts.edu
Sat Feb 26 09:16:05 EST 2005


Log message for revision 29309:
  Finished my work on the apidoc tool. In this last milestone I rewrote the 
  ZCML parser once again and rearranged the documentation.
  
  Please let me know, if you have suggestions for improvements.
  
  

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  U   Zope3/trunk/doc/TODO.txt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/directive.pt
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/ftests.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/tests.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/codemodule.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/function.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/module.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/doc/CHANGES.txt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -29,6 +29,8 @@
 
       - API doctool has received some upgrades:
 
+        * Code Browser now also shows interfaces and text files.
+
         * A new `bookmodule` compiles all our README.txt and other text
           documentation files to a nice book-like format.
 
@@ -285,6 +287,9 @@
 
       - Several changes have been made to the API doc tool:
 
+        + Rewrote the ZCML file parser to reuse the zope.configuration
+          code. This makes the code much more stable and predictable.
+
         + General inspection utilities are now *publically* available in the
           modules: utilities, interface, component, presentation,
           classregistry

Modified: Zope3/trunk/doc/TODO.txt
===================================================================
--- Zope3/trunk/doc/TODO.txt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/doc/TODO.txt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -9,14 +9,6 @@
 
 - Fix final issues relating the new PAS and related UIs
 
-- The API Doc Tool needs to be updated to reflect latest design changes
-
-  o Better support for multi-adapters
-
-  o Presentation components are now just adapters
-
-  o Services will be utilities
-
 - Develop a generic browser:form directive
 
 - Support for iterable sources

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,322 @@
+=========================
+Code Documentation Module
+=========================
+
+The code documentation module package
+
+  >>> from zope.app.apidoc import codemodule
+
+provides systematic and autogenerated documentation about the content of your
+Zope 3 related Python packages. The code module can be created like this: 
+
+  >>> cm = codemodule.codemodule.CodeModule()
+
+  >>> cm.getDocString()
+  u'Zope 3 root.'
+
+
+This object extends the `codemodule.module.Module` class, since it can be seen
+as some sort of root package. However, its sementacs are obviously a bit
+different:
+
+  >>> cm.getFileName()
+  ''
+  >>> cm.getPath()
+  ''
+
+  >>> cm.keys()
+  []
+
+
+Module
+------
+
+The `Module` class represents a Python module or package in the documentation
+tree. It can be easily setup by simply passing the parent module, the
+module name (not the entire Python path) and the Python module instance
+itself:
+
+  >>> import zope.app.apidoc
+  >>> module = codemodule.module.Module(None, 'apidoc', zope.app.apidoc)
+
+We can now get some of the common module attributes via accessor methods:
+
+  >>> module.getDocString() is None
+  True
+
+  >>> fname = module.getFileName()
+  >>> fname = fname.replace('\\\\', '/') # Fix for Windows users
+  >>> 'zope/app/apidoc/__init__.py' in fname
+  True
+
+  >>> module.getPath()
+  'zope.app.apidoc'
+
+The setup for creating the sub module and class tree is automatically
+called during initialization, so that the sub-objects are available as
+soon as you have the object:
+
+  >>> keys = module.keys()
+  >>> 'codemodule' in keys
+  True
+  >>> 'meta.zcml' in keys
+  True
+
+  >>> print module['browser'].getPath()
+  zope.app.apidoc.browser
+
+Now, the ``get(key, default=None)`` is actually much smarter than you might
+originally suspect, since it can actually get to more objects than it
+promises. If a key is not found in the module's children, it tries to
+import the key as a module relative to this module.
+
+For example, while `tests` directories are not added to the module and
+classes hierarchy (since they do not provide or implement any API), we can
+still get to them:
+
+  >>> print module['tests'].getPath()
+  zope.app.apidoc.tests
+
+  >>> names = module['tests'].keys()
+  >>> names.sort()
+  >>> names
+  ['Root', 'rootLocation', 'setUp', 'test_suite']
+
+
+Classes
+-------
+
+Setting up a class for documentation is not much harder. You only need to
+provide an object providing `IModule` as a parent, the name and the klass
+itself:
+
+  >>> import zope.app.apidoc.apidoc
+  >>> module = codemodule.module.Module(
+  ...     None, 'apidoc', zope.app.apidoc.apidoc)
+  >>> klass = codemodule.class_.Class(module, 'APIDocumentation',
+  ...                                 zope.app.apidoc.apidoc.APIDocumentation)
+
+This class provides data about the class in an accessible format. The
+Python path and doc string are easily retrieved using::
+
+  >>> klass.getPath()
+  'zope.app.apidoc.apidoc.APIDocumentation'
+
+  >>> klass.getDocString()[:41]
+  'Represent the complete API Documentation.'
+
+A list of base classes can also be retrieved. The list only includes
+direct bases, so if we have class `Blah`, which extends `Bar`, which
+extends `Foo`, then the base of `Blah` is just `Bar`. In our example this
+looks like this:
+
+  >>> klass.getBases()
+  (<class 'zope.app.apidoc.utilities.ReadContainerBase'>,)
+
+In the other direction, you can get a list of known subclasses.  The list
+only includes those subclasses that are registered with the global
+`classRegistry` dictionary. In our example:
+
+  >>> class APIDocSubclass(zope.app.apidoc.apidoc.APIDocumentation):
+  ...   pass
+
+  >>> klass2 = codemodule.class_.Class(module, 'APIDocSubclass', APIDocSubclass)
+
+  >>> klass.getKnownSubclasses()
+  [<class 'APIDocSubclass'>]
+
+For a more detailed analysis, you can also retrieve the public attributes
+and methods of this class::
+
+  >>> klass.getAttributes()
+  []
+
+  >>> klass.getMethods()[0]
+  ('get', <unbound method APIDocumentation.get>, None)
+
+
+Let's have a closer look at the `getAttributes()` method. First we create an
+interface called `IBlah` that is implemented by the class `Blah`:
+
+  >>> import zope.interface
+  >>> class IBlah(zope.interface.Interface):
+  ...      foo = zope.interface.Attribute('Foo')
+
+  >>> class Blah(object):
+  ...      zope.interface.implements(IBlah)
+  ...      foo = 'f'
+  ...      bar = 'b'
+  ...      _blah = 'l'
+
+The `Blah` class also implements a public and private attribute that is not
+listed in the interface. Now we create the class documentation wrapper:
+
+  >>> klass = codemodule.class_.Class(module, 'Blah', Blah)
+
+  >>> pprint(klass.getAttributes())
+  [('bar', 'b', None),
+   ('foo', 'f', <InterfaceClass __builtin__.IBlah>)]
+
+So, the function returns a list of tuples of the form (name, value,
+interface), where the interface is the interface in which the attribute was
+declared. The interface is `None`, if the attribute was not declared. Also
+note that attributes starting with an underscore are ignored.
+
+
+Let's now have a look at how methods are looked up returned. So we create a
+new `IBlah` interface, this time describing methods, and then its
+implementation `Blah`, which has some other additional methods:
+
+  >>> class IBlah(zope.interface.Interface):
+  ...      def foo(): pass
+
+  >>> class Blah(object):
+  ...      zope.interface.implements(IBlah)
+  ...
+  ...      def foo(self): 
+  ...          pass
+  ...      def bar(self): 
+  ...          pass
+  ...      def _blah(self): 
+  ...          pass
+
+Now we create the class documentation wrapper:
+
+  >>> klass = codemodule.class_.Class(module, 'Blah', Blah)
+
+and get the method documenation:
+
+  >>> pprint(klass.getMethods())
+  [('bar', <unbound method Blah.bar>, None),
+   ('foo', <unbound method Blah.foo>, <InterfaceClass __builtin__.IBlah>)]
+
+
+Function
+--------
+
+Function are pretty much documented in the same way as all other code
+documentation objects and provides a similar API to the classes. A function
+documenation object is quickly created:
+
+  >>> func = codemodule.function.Function(
+  ...     module, 'handleNamespace',
+  ...     zope.app.apidoc.apidoc.handleNamespace)
+
+This class provides data about the function in an accessible format. The
+Python path, signature and doc string are easily retrieved using:
+
+  >>> func.getPath()
+  'zope.app.apidoc.apidoc.handleNamespace'
+
+  >>> func.getSignature()
+  '(ob, name)'
+
+  >>> func.getDocString()
+  'Used to traverse to an API Documentation.'
+
+For a more detailed analysis, you can also retrieve the attributes of the
+function
+
+  >>> func.getAttributes()
+  []
+
+but this function has none as most functions. So let's create a new function 
+
+  >>> def foo(bar=1):
+  ...     pass
+
+  >>> func = codemodule.function.Function(module, 'foo', foo)
+
+which originally does not have any attributes
+
+  >>> func.getAttributes()
+  []
+
+but if we add an attribute, it will be listed:
+
+  >>> foo.blah = 1
+  >>> func.getAttributes()
+  [('blah', 1)]
+
+
+Text File
+---------
+
+Text files represent plain-text documentation files like this one. Once we
+have a text file documentation object
+
+  >>> import os
+  >>> path = os.path.join(os.path.dirname(codemodule.__file__), 'README.txt')
+  >>> readme = codemodule.text.TextFile(path, 'README.txt', module)
+
+we can ask it for the content of the file:
+
+  >>> print readme.getContent()[:77]
+  =========================
+  Code Documentation Module
+  =========================
+
+
+ZCML File
+---------
+
+ZCML file documentation objects present configuration files and parse the file
+content to provide some advanced markup. The object is easily instantiated:
+
+  >>> path = os.path.join(os.path.dirname(codemodule.__file__), 
+  ...                     'configure.zcml')
+  >>> module = codemodule.module.Module(None, 'zope.app.apidoc.codemodule', 
+  ...                                   zope.app.apidoc.codemodule)
+
+  >>> zcml = codemodule.zcml.ZCMLFile(path, module)
+
+The interesting attribute of the object is the `rootElement`, since it
+contains the root XML element and thus the entire XML tree. The `rootElement`
+attribute is a lazy property, so that it is not loaded until accessed for the
+first time:
+
+  >>> root = zcml.rootElement
+  >>> root
+  <Directive (u'http://namespaces.zope.org/zope', u'configure')>
+
+A directive component has some interesting atrributes, such as the name,
+
+  >>> root.name
+  (u'http://namespaces.zope.org/zope', u'configure')
+
+the schema that describes the directive,
+
+  >>> root.schema
+  <InterfaceClass zope.configuration.zopeconfigure.IZopeConfigure>
+
+the attributes of the XML element,
+
+  >>> dict(root.attrs)
+  {}
+
+the configuration context for the directive, which can be used to resolve
+objects and/or generate absolute paths of files,
+
+  >>> root.context #doctest:+ELLIPSIS
+  <zope.configuration.config.ConfigurationMachine object at ...>
+
+the parser info object,
+
+  >>> root.info
+  File
+  "/opt/zope/Zope3/Zope3-Fresh/src/zope/app/apidoc/codemodule/configure.zcml",
+  line 1.0-53.0
+
+the sub-directives,
+
+  >>> root.subs[:2]
+  [<Directive (u'http://namespaces.zope.org/zope', u'class')>, 
+   <Directive (u'http://namespaces.zope.org/zope', u'class')>]
+
+and finally a list of all prefixes.
+
+  >>> pprint(root.prefixes)
+  {u'http://namespaces.zope.org/apidoc': u'apidoc',
+   u'http://namespaces.zope.org/browser': u'browser',
+   u'http://namespaces.zope.org/zope': None}
+


Property changes on: Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -1,121 +1 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Code Documentation Module
-
-This module is able to take a dotted name of any class and display
-documentation for it.
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-from zope.interface import Interface, implements
-from zope.app import zapi
-from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.app.location.interfaces import ILocation
-
-from zope.app.apidoc.interfaces import IDocumentationModule
-from zope.app.apidoc.classregistry import safe_import
-from zope.app.apidoc.codemodule.module import Module
-
-
-class IAPIDocRootModule(Interface):
-    """Marker interface for utilities that represent class browser root
-    modules.
-
-    The utilities will be simple strings, representing the modules Python
-    dotted name.
-    """ 
-
-
-class CodeModule(Module):
-    """Represent the Documentation of any possible source code in the packages.
-
-    This object extends a module, since it can be seen as some sort of root
-    module. However, its sementacs are obviously a bit different::
-
-      >>> cm = CodeModule()
-
-      >>> cm.getDocString()
-      u'Zope 3 root.'
-      >>> cm.getFileName()
-      ''
-      >>> cm.getPath()
-      ''
-
-      >>> names = cm.keys()
-      >>> names.sort()
-      >>> names
-      [u'zope']
-    """
-    implements(IDocumentationModule)
-
-    # See zope.app.apidoc.interfaces.IDocumentationModule
-    title = _('Code Browser')
-
-    # See zope.app.apidoc.interfaces.IDocumentationModule
-    description = _("""
-    This module allows you to get an overview of the modules and classes
-    defined in the Zope 3 framework and its supporting packages. There are
-    two methods to navigate through the modules to find the classes you are
-    interested in.
-
-    The first method is to type in some part of the Python path of the class
-    and the module will look in the class registry for matches. The menu will
-    then return with a list of these matches.
-
-    The second method is to click on the "Browse Zope Source" link. In the
-    main window, you will see a directory listing with the root Zope 3
-    modules. You can click on the module names to discover their content. If a
-    class is found, it is represented as a bold entry in the list.
-
-    The documentation contents of a class provides you with an incredible
-    amount of information. Not only does it tell you about its base classes,
-    implemented interfaces, attributes and methods, but it also lists the
-    interface that requires a method or attribute to be implemented and the
-    permissions required to access it.
-    """)
-    def __init__(self):
-        """Initialize object."""
-        super(CodeModule, self).__init__(None, '', None, False)
-        self.__isSetup = False
-
-    def setup(self):
-        """Setup module and class tree."""
-        if self.__isSetup:
-            return
-        for name, mod in zapi.getUtilitiesFor(IAPIDocRootModule):
-            self._children[name] = Module(self, name, safe_import(mod))
-        self.__isSetup = True
-
-    def getDocString(self):
-        """See Module class."""
-        return _('Zope 3 root.')
-
-    def getFileName(self):
-        """See Module class."""
-        return ''
-
-    def getPath(self):
-        """See Module class."""
-        return ''
-
-    def get(self, key, default=None):
-        """See zope.app.container.interfaces.IReadContainer."""
-        self.setup()
-        return super(CodeModule, self).get(key, default)
-
-    def items(self):
-        """See zope.app.container.interfaces.IReadContainer."""
-        self.setup()
-        return super(CodeModule, self).items()
+# Make a package

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,378 @@
+====================================
+Code Browser Presentation Components
+====================================
+
+This document describes the API of the views complementing the varius code
+browser documentation components. The views can be found in
+
+  >>> from zope.app.apidoc.codemodule import browser
+
+We will also need the code browser documentation module:
+
+  >>> from zope.app import zapi
+  >>> from zope.app.apidoc.interfaces import IDocumentationModule
+  >>> cm = zapi.getUtility(IDocumentationModule, 'Code')
+
+The `zope` package is already registered and available with the code module.
+
+
+Module Details
+--------------
+
+The module details are easily created, since we can just use the traversal
+process to get a module documentation object:
+
+  >>> details = browser.module.ModuleDetails()
+  >>> details.context = zapi.traverse(cm, 
+  ...     'zope/app/apidoc/codemodule/codemodule')
+  >>> from zope.publisher.browser import TestRequest
+  >>> details.request = TestRequest()
+
+`getDoc()`
+~~~~~~~~~~
+
+Get the doc string of the module formatted in STX or ReST.
+
+  >>> print details.getDoc().strip()
+  <div class="document">
+  <p>Code Documentation Module</p>
+  <p>This module is able to take a dotted name of any class and display
+  documentation for it.</p>
+  </div>
+
+`getEntries(columns=True)`
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Return info objects for all modules and classes in this module.
+
+  >>> pprint(details.getEntries(False))
+  [{'isclass': True,
+    'isfunction': False,
+    'isinterface': False,
+    'ismodule': False,
+    'istextfile': False,
+    'iszcmlfile': False,
+    'name': 'CodeModule',
+    'url': 'http://127.0.0.1/zope/app/apidoc/codemodule/codemodule/CodeModule'}]
+
+`getBreadCrumbs()`
+~~~~~~~~~~~~~~~~~~
+
+Create breadcrumbs for the module path.
+
+We cannot reuse the the system's bread crumbs, since they go all the
+way up to the root, but we just want to go to the root module.
+
+  >>> pprint(details.getBreadCrumbs())
+  [{'name': u'[top]',
+    'url': 'http://127.0.0.1'},
+   {'name': u'zope',
+    'url': 'http://127.0.0.1/zope'},
+   {'name': 'app',
+    'url': 'http://127.0.0.1/zope/app'},
+   {'name': 'apidoc',
+    'url': 'http://127.0.0.1/zope/app/apidoc'},
+   {'name': 'codemodule',
+    'url': 'http://127.0.0.1/zope/app/apidoc/codemodule'},
+   {'name': 'codemodule',
+    'url': 'http://127.0.0.1/zope/app/apidoc/codemodule/codemodule'}]
+
+
+Class Details
+-------------
+
+The class details are easily created, since we can just use the traversal
+process to get a class documentation object:
+
+  >>> details = browser.class_.ClassDetails()
+  >>> details.context = zapi.traverse(
+  ...     cm, 'zope/app/apidoc/codemodule/codemodule/CodeModule')
+
+  >>> details.request = TestRequest()
+
+Now that we have the details class we can just access the various methods:
+
+`getBases()`
+~~~~~~~~~~~
+
+Get all bases of this class.
+
+  >>> pprint(details.getBases())
+  [{'path': 'zope.app.apidoc.codemodule.module.Module',
+    'url': 'http://127.0.0.1/zope/app/apidoc/codemodule/module/Module'}]
+
+`getKnownSubclasses()`
+~~~~~~~~~~~~~~~~~~~~~~
+Get all known subclasses of this class.
+
+  >>> details.getKnownSubclasses()
+  []
+
+`_listClasses(classes)`
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Prepare a list of classes for presentation.
+
+  >>> import zope.app.apidoc.apidoc
+  >>> import zope.app.apidoc.codemodule.codemodule
+
+  >>> pprint(details._listClasses([
+  ...       zope.app.apidoc.apidoc.APIDocumentation,
+  ...       zope.app.apidoc.codemodule.codemodule.Module]))
+  [{'path': 'zope.app.apidoc.apidoc.APIDocumentation',
+    'url': 'http://127.0.0.1/zope/app/apidoc/apidoc/APIDocumentation'},
+   {'path': 'zope.app.apidoc.codemodule.module.Module',
+    'url': 'http://127.0.0.1/zope/app/apidoc/codemodule/module/Module'}]
+
+`getBaseURL()`
+~~~~~~~~~~~~~~
+
+Return the URL for the API Documentation Tool.
+
+Note that the following output is a bit different than usual, since
+we have not setup all path elements.
+
+  >>> details.getBaseURL()
+  'http://127.0.0.1'
+
+`getInterfaces()`
+~~~~~~~~~~~~~~~~~
+
+Get all implemented interfaces (as paths) of this class.
+
+  >>> pprint(details.getInterfaces())
+  ['zope.app.apidoc.interfaces.IDocumentationModule',
+   'zope.app.location.interfaces.ILocation',
+   'zope.app.apidoc.codemodule.interfaces.IModuleDocumentation',
+   'zope.app.container.interfaces.IReadContainer']
+
+`getAttributes()`
+~~~~~~~~~~~~~~~~~
+
+Get all attributes of this class.
+
+  >>> pprint(details.getAttributes()[1])
+  {'interface': 'zope.app.apidoc.interfaces.IDocumentationModule',
+   'name': 'title',
+   'read_perm': None,
+   'type': 'MessageID',
+   'type_link': 'zope/i18nmessageid/messageid/MessageID',
+   'value': "u'Code Browser'",
+   'write_perm': None}
+
+`getMethods()`
+~~~~~~~~~~~~~~
+Get all methods of this class.
+
+  >>> pprint(details.getMethods()[-2:])
+  [{'doc': u'<div class="document">\nSetup module and class tree.</div>\n',
+    'interface': None,
+    'name': 'setup',
+    'read_perm': None,
+    'signature': '()',
+    'write_perm': None},
+   {'doc': u'',
+    'interface': 'zope.interface.common.mapping.IEnumerableMapping',
+    'name': 'values',
+    'read_perm': None,
+    'signature': '()',
+    'write_perm': None}]
+
+`getDoc()`
+~~~~~~~~~~
+
+Get the doc string of the class STX formatted.
+
+  >>> print details.getDoc()[:-1]
+  <div class="document">
+  Represent the code browser documentation root</div>
+
+
+Function Details
+----------------
+
+This is the same deal as before, use the path to generate the function
+documentation component:
+
+  >>> details = browser.function.FunctionDetails()
+  >>> details.context = zapi.traverse(cm, 
+  ...     'zope/app/apidoc/codemodule/browser/tests/foo')
+  >>> details.request = TestRequest()
+
+Here are the methods:
+
+`getDocString()`
+~~~~~~~~~~~~~~~~
+
+Get the doc string of the function in a rendered format.
+
+  >>> details.getDocString()
+  u'<div class="document">\nThis is the foo function.</div>\n'
+
+`getAttributes()`
+~~~~~~~~~~~~~~~~~
+
+Get all attributes of this function.
+
+  >>> attr = details.getAttributes()[0]
+  >>> pprint(attr)
+  {'name': 'deprecated',
+   'type': 'bool',
+   'type_link': '__builtin__/bool',
+   'value': 'True'}
+
+
+Text File Details
+-----------------
+
+This is the same deal as before, use the path to generate the text file
+documentation component:
+
+  >>> details = browser.text.TextFileDetails()
+  >>> details.context = zapi.traverse(cm, 
+  ...     'zope/app/apidoc/codemodule/README.txt')
+  >>> details.request = TestRequest()
+
+Here are the methods:
+
+`renderedContent()`
+~~~~~~~~~~~~~~~~~~~
+
+Render the file content to HTML.
+
+  >>> print details.renderedContent()[54:102]
+  <h1 class="title">Code Documentation Module</h1>
+
+
+ZCML File and Directive Details
+-------------------------------
+
+The ZCML file details are a bit different, since there is no view class for
+ZCML files, just a template. The template then uses the directive details to
+provide all the view content:
+
+  >>> details = browser.zcml.DirectiveDetails()
+  >>> zcml = zapi.traverse(cm, 
+  ...     'zope/app/apidoc/codemodule/configure.zcml')
+  >>> details.context = zcml.rootElement
+  >>> details.request = TestRequest()
+  >>> details.__parent__ = details.context
+
+Here are the methods for the directive details:
+
+`fullTagName()`
+~~~~~~~~~~~~~~~
+
+Return the name of the directive, including prefix, if applicable.
+
+  >>> details.fullTagName()
+  u'configure'
+
+`line()`
+~~~~~~~~
+
+Return the line (as a string) at which this directive starts.
+
+  >>> details.line()
+  '1'
+
+`highlight()`
+~~~~~~~~~~~~~
+
+It is possible to highlight a directive by passing the `line` variable as a
+request variable. If the value of `line` matches the output of `line()`, this
+method returns 'highlight' and otherwise ''. 'highlight' is a CSS class that
+places a colored box around the directive.
+
+  >>> details.highlight()
+  ''
+
+  >>> details.request = TestRequest(line='1')
+  >>> details.highlight()
+  'highlight'
+
+`url()`
+~~~~~~~
+
+Returns the URL of the directive docuemntation in the ZCML documentation
+module.
+
+  >>> details.url()
+  u'http://127.0.0.1/../ZCML/ALL/configure/index.html'
+
+The result is a bit strange, since the ZCML Documentation module is the
+containment root.
+
+`ifaceURL(value, field, rootURL)`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method converts the string value of the field to an interface and then
+crafts a documentation URL for it:
+
+  >>> from zope.configuration.fields import GlobalInterface
+  >>> field = GlobalInterface()
+
+  >>> details.ifaceURL('.interfaces.IZCMLFile', field, '')
+  '/../Interface/zope.app.apidoc.codemodule.interfaces.IZCMLFile/apiindex.html'
+
+`objectURL(value, field, rootURL)`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method converts the string value of the field to an object and then
+crafts a documentation URL for it:
+
+  >>> from zope.configuration.fields import GlobalObject
+  >>> field = GlobalObject()
+
+  >>> details.objectURL('.interfaces.IZCMLFile', field, '')
+  '/../Interface/zope.app.apidoc.codemodule.interfaces.IZCMLFile/apiindex.html'
+
+  >>> details.objectURL('.zcml.ZCMLFile', field, '')
+  '/zope/app/apidoc/codemodule/zcml/ZCMLFile/index.html'
+
+`attributes()`
+~~~~~~~~~~~~~~
+
+Returns a list of info dictionaries representing all the attributes in the
+directive. If the directive is the root directive, all namespace declarations
+will be listed too.
+
+  >>> pprint(details.attributes())
+  [{'name': 'xmlns',
+    'url': None,
+    'value': u'http://namespaces.zope.org/zope',
+    'values': []},
+   {'name': u'xmlns:apidoc',
+    'url': None,
+    'value': u'http://namespaces.zope.org/apidoc',
+    'values': []},
+   {'name': u'xmlns:browser',
+    'url': None,
+    'value': u'http://namespaces.zope.org/browser',
+    'values': []}]
+
+  >>> details.context = details.context.subs[0]
+  >>> pprint(details.attributes())
+  [{'name': u'class',
+    'url': 
+        'http://127.0.0.1/zope/app/apidoc/codemodule/module/Module/index.html',
+    'value': u'.module.Module',
+    'values': []}]
+
+`hasSubDirectives()`
+~~~~~~~~~~~~~~~~~~~~
+
+Returns `True`, if the directive has subdirectives; otherwise `False` is
+returned.
+
+  >>> details.hasSubDirectives() 
+  True
+
+`getElements()`
+~~~~~~~~~~~~~~~
+
+Returns a list of all sub-directives:
+
+  >>> details.getElements()
+  [<Directive (u'http://namespaces.zope.org/zope', u'allow')>]
+ 
\ No newline at end of file


Property changes on: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -36,62 +36,18 @@
     """Represents the details of the class."""
 
     def getBases(self):
-        """Get all bases of this class.
-
-        Example::
-
-          The class we are using for this view is
-          zope.app.apidoc.classmodule.ClassModule.
-
-          >>> import pprint
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          >>> pprint.pprint(view.getBases())
-          [{'path': 'zope.app.apidoc.classmodule.Module',
-            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/Module'}]
-        """
+        """Get all bases of this class."""
         return self._listClasses(self.context.getBases())
 
 
     def getKnownSubclasses(self):
-        """Get all known subclasses of this class.
-
-        Example::
-
-          The class we are using for this view is
-          zope.app.apidoc.classmodule.ClassModule.
-
-          >>> import pprint
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          >>> pprint.pprint(view.getKnownSubclasses())
-          []
-        """
+        """Get all known subclasses of this class."""
         entries = self._listClasses(self.context.getKnownSubclasses())
         entries.sort(lambda x, y: cmp(x['path'], y['path']))
         return entries
 
     def _listClasses(self, classes):
-        """Prepare a list of classes for presentation.
-
-        Example::
-
-          >>> import pprint
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-          >>> import zope.app.apidoc
-          >>> import zope.app.apidoc.classmodule
-
-          >>> pprint.pprint(view._listClasses([
-          ...       zope.app.apidoc.APIDocumentation,
-          ...       zope.app.apidoc.classmodule.Module]))
-          [{'path': 'zope.app.apidoc.APIDocumentation',
-            'url': 'http://127.0.0.1/zope/app/apidoc/APIDocumentation'},
-           {'path': 'zope.app.apidoc.classmodule.Module',
-            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/Module'}]
-        """
+        """Prepare a list of classes for presentation."""
         info = []
         codeModule = zapi.getUtility(IDocumentationModule, "Code")
         for cls in classes:
@@ -113,66 +69,18 @@
 
 
     def getBaseURL(self):
-        """Return the URL for the API Documentation Tool.
-
-        Example::
-
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          Note that the following output is a bit different than usual, since
-          we have not setup all path elements.
-
-          >>> view.getBaseURL()
-          'http://127.0.0.1'
-        """
+        """Return the URL for the API Documentation Tool."""
         m = zapi.getUtility(IDocumentationModule, "Code")
         return zapi.absoluteURL(zapi.getParent(m), self.request)
 
 
     def getInterfaces(self):
-        """Get all implemented interfaces (as paths) of this class.
-
-        Example::
-
-          The class we are using for this view is
-          zope.app.apidoc.classmodule.ClassModule.
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          >>> pprint(view.getInterfaces())
-          ['zope.app.apidoc.interfaces.IDocumentationModule',
-           'zope.app.location.interfaces.ILocation',
-           'zope.app.apidoc.classmodule.IModuleDocumentation',
-           'zope.app.container.interfaces.IReadContainer']
-        """
+        """Get all implemented interfaces (as paths) of this class."""
         return map(getPythonPath, self.context.getInterfaces())
 
 
     def getAttributes(self):
-        """Get all attributes of this class.
-
-        Example::
-
-          The class we are using for this view is
-          zope.app.apidoc.classmodule.ClassModule.
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          >>> attr = view.getAttributes()[1]
-          >>> pprint(attr)
-          [('interface', 'zope.app.apidoc.interfaces.IDocumentationModule'),
-           ('name', 'title'),
-           ('read_perm', None),
-           ('type', 'MessageID'),
-           ('type_link', 'zope.i18nmessageid.messageid.MessageID'),
-           ('value', "u'Classes'"),
-           ('write_perm', None)]
-        """
+        """Get all attributes of this class."""
         attrs = []
         for name, attr, iface in self.context.getAttributes():
             entry = {'name': name,
@@ -190,34 +98,7 @@
 
 
     def getMethods(self):
-        """Get all methods of this class.
-
-        Example::
-
-          The class we are using for this view is
-          zope.app.apidoc.classmodule.ClassModule.
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          >>> methods = view.getMethods()
-          >>> pprint(methods[-2:])
-          [[('doc', u''),
-            ('interface',
-             'zope.interface.common.mapping.IEnumerableMapping'),
-            ('name', 'keys'),
-            ('read_perm', None),
-            ('signature', '()'),
-            ('write_perm', None)],
-           [('doc', u''),
-            ('interface',
-             'zope.interface.common.mapping.IEnumerableMapping'),
-            ('name', 'values'),
-            ('read_perm', None),
-            ('signature', '()'),
-            ('write_perm', None)]]
-        """
+        """Get all methods of this class."""
         methods = []
         # remove the security proxy, so that `attr` is not proxied. We could
         # unproxy `attr` for each turn, but that would be less efficient.
@@ -237,19 +118,7 @@
 
 
     def getDoc(self):
-        """Get the doc string of the class STX formatted.
-
-        Example::
-
-          The class we are using for this view is
-          zope.app.apidoc.classmodule.ClassModule.
-
-          >>> from tests import getClassDetailsView
-          >>> view = getClassDetailsView()
-
-          >>> print view.getDoc()[23:80]
-          <p>Represent the Documentation of any possible class.</p>
-        """
+        """Get the doc string of the class STX formatted."""
         return renderText(self.context.getDocString() or '',
                           zapi.getParent(self.context).getPath())
 

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml	2005-02-26 14:16:04 UTC (rev 29309)
@@ -3,7 +3,7 @@
     xmlns:zope="http://namespaces.zope.org/zope">
 
   <page
-      for="..CodeModule"
+      for="..codemodule.CodeModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".menu.Menu"
       name="menu.html"
@@ -39,7 +39,7 @@
 
   <!-- ZCML File -->
   <page
-      for="..interfaces.IConfiguration"
+      for="..interfaces.IZCMLFile"
       name="index.html"
       template="zcmlfile_index.pt"
       permission="zope.View"/>
@@ -47,16 +47,8 @@
   <page
       name="display"
       for="..interfaces.IDirective"
-      template="displayDirective.pt"
-      class=".zcml.DisplayDirective"
+      template="directive.pt"
+      class=".zcml.DirectiveDetails"
       permission="zope.ManageContent"/>
 
-  <page
-      name="display"
-      for="..interfaces.IComment"
-      template="displayComment.pt"
-      class=".zcml.DisplayComment"
-      permission="zope.ManageContent"/>
-
-
 </configure>

Copied: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/directive.pt (from rev 29294, Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt)
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt	2005-02-25 03:38:07 UTC (rev 29294)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/directive.pt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,47 @@
+<div class="directive"
+     tal:attributes="class string:directive ${view/highlight}">
+  <a name="" tal:attributes="name view/line" />
+  &lt;<a href="" tal:attributes="href view/url"
+    ><span class="tagName" tal:content="view/fullTagName">tagName</span></a>
+  <br />
+
+  <span tal:repeat="attr view/attributes">
+    &nbsp;&nbsp;&nbsp;
+    <span tal:condition="not: attr/values">
+      <span class="attributeName" tal:content="attr/name">
+        name
+      </span>="<a tal:attributes="href attr/url"><span
+           class="attributeValue" 
+           tal:content="attr/value">value</span></a>"
+    </span>
+    <span tal:condition="attr/values">
+      <span class="attributeName" tal:content="attr/name">
+        name
+      </span>="
+      <div tal:repeat="entry attr/values">
+        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+        <a tal:attributes="href entry/url"><span
+           class="attributeValue" 
+           tal:content="entry/value">value</span></a>
+     </div>
+     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
+    </span>
+    <br />
+  </span>
+  &nbsp;&nbsp;&nbsp;
+  <span tal:condition="view/hasSubDirectives">&gt;</span>
+  <span tal:condition="not:view/hasSubDirectives">/&gt;</span>
+</div>  
+
+<div style="margin-left: 1.5em;">
+  <tal:block repeat="element view/getElements">
+    <tal:block replace="structure element/@@display" />
+  </tal:block>
+</div>
+
+<div class="directive"
+     tal:condition="view/hasSubDirectives">
+  &lt;/<span class="tagName" 
+             tal:content="view/fullTagName" />&gt;
+
+</div>

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -1,30 +0,0 @@
-<div class="directive">
-  &lt;<!--a href="" tal:attributes="href view/url"
-    --><span class="tagName" tal:content="view/fullTagName">tagName</span><!--/a-->
-  <br />
-
-  <span tal:repeat="attr view/attributes">
-    &nbsp;&nbsp;&nbsp;
-    <span class="attributeName" tal:content="attr/name">
-      name
-    </span>="<!--a tal:attributes="href attr/url"--><span
-         class="attributeValue" 
-         tal:content="attr/value">value</span><!--/a-->"
-    <br />
-  </span>
-  &nbsp;&nbsp;&nbsp;
-  <span tal:condition="view/hasSubDirectives">&gt;</span>
-  <span tal:condition="not:view/hasSubDirectives">/&gt;</span>
-</div>  
-
-<div style="margin-left: 1.5em;">
-  <tal:block repeat="element view/getElements">
-    <tal:block replace="structure element/@@display" />
-  </tal:block>
-</div>
-
-<div class="directive"
-     tal:condition="view/hasSubDirectives">
-  &lt;/<span class="tagName" 
-             tal:content="context/getFullTagName" />&gt;
-</div>

Copied: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/ftests.py (from rev 29294, Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py)
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py	2005-02-25 03:38:07 UTC (rev 29294)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/ftests.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,102 @@
+##############################################################################
+#
+# Copyright (c) 2003, 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Functional Tests for Code Documentation Module.
+
+$Id$
+"""
+import unittest
+from zope.app.testing.functional import BrowserTestCase
+
+class CodeModuleTests(BrowserTestCase):
+    """Just a couple of tests ensuring that the templates render."""
+
+    def testMenu(self):
+        response = self.publish('/++apidoc++/Code/menu.html', 
+                                basic='mgr:mgrpw')
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.assert_(body.find('Zope Source') > 0)
+        self.checkForBrokenLinks(body, '/++apidoc++/Code/menu.html',
+                                 basic='mgr:mgrpw')
+
+    def testMenuCodeFinder(self):
+        response = self.publish('/++apidoc++/Code/menu.html',
+                                basic='mgr:mgrpw',
+                                form={'path': 'Code', 'SUBMIT': 'Find'})
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.assert_(
+            body.find('zope.app.apidoc.codemodule.codemodule.CodeModule') > 0)
+        self.checkForBrokenLinks(body, '/++apidoc++/Code/menu.html',
+                                 basic='mgr:mgrpw')
+
+    def testModuleDetailsView(self):
+        response = self.publish('/++apidoc++/Code/zope/app/apidoc/apidoc',
+                                basic='mgr:mgrpw')
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.assert_(body.find('Zope 3 API Documentation') > 0)
+        self.checkForBrokenLinks(
+            body, '/++apidoc++/Code/zope/app/apidoc/apidoc', basic='mgr:mgrpw')
+
+    def testClassDetailsView(self):
+        response = self.publish(
+            '/++apidoc++/Code/zope/app/apidoc/apidoc/APIDocumentation',
+            basic='mgr:mgrpw')
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.assert_(body.find('Represent the complete API Documentation.') > 0)
+        self.checkForBrokenLinks(
+            body, '/++apidoc++/Code/zope/app/apidoc/apidoc/APIDocumentation',
+            basic='mgr:mgrpw')
+
+    def testFunctionDetailsView(self):
+        response = self.publish(
+            '/++apidoc++/Code/zope/app/apidoc/apidoc/handleNamespace',
+            basic='mgr:mgrpw')
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.assert_(body.find('handleNamespace(ob, name)') > 0)
+        self.checkForBrokenLinks(
+            body, '/++apidoc++/Code/zope/app/apidoc/apidoc/handleNamesapce',
+            basic='mgr:mgrpw')
+
+    def testTextFileDetailsView(self):
+        response = self.publish(
+            '/++apidoc++/Code/zope/app/apidoc/README.txt/index.html',
+            basic='mgr:mgrpw')
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.checkForBrokenLinks(
+            body, '/++apidoc++/Code/zope/app/apidoc/README.txt/index.html',
+            basic='mgr:mgrpw')
+
+    def testZCMLFileDetailsView(self):
+        response = self.publish(
+            '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
+            basic='mgr:mgrpw')
+        self.assertEqual(response.getStatus(), 200)
+        body = response.getBody()
+        self.checkForBrokenLinks(
+            body, '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
+            basic='mgr:mgrpw')
+        
+
+def test_suite():
+    return unittest.TestSuite((
+        unittest.makeSuite(CodeModuleTests),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -23,36 +23,13 @@
     """Represents the details of the function."""
 
     def getDocString(self):
-        r"""Get the doc string of the function in a rendered format.
-
-        Example::
-
-          >>> from tests import getFunctionDetailsView
-          >>> view = getFunctionDetailsView()
-
-          >>> view.getDocString()
-          u'<p>This is the foo function.</p>\n'
-        """
+        """Get the doc string of the function in a rendered format."""
         return renderText(self.context.getDocString() or '',
                           zapi.getParent(self.context).getPath())
 
 
     def getAttributes(self):
-        """Get all attributes of this function.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getFunctionDetailsView
-          >>> view = getFunctionDetailsView()
-
-          >>> attr = view.getAttributes()[0]
-          >>> pprint(attr)
-          [('name', 'deprecated'),
-           ('type', 'bool'),
-           ('type_link', '__builtin__/bool'),
-           ('value', 'True')]
-        """
+        """Get all attributes of this function."""
         return [{'name': name,
                  'value': `attr`,
                  'type': type(attr).__name__,

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -28,29 +28,14 @@
 from zope.app.apidoc.codemodule.class_ import Class
 from zope.app.apidoc.codemodule.function import Function
 from zope.app.apidoc.codemodule.text import TextFile
-from zope.app.apidoc.codemodule.zcml import Configuration
+from zope.app.apidoc.codemodule.zcml import ZCMLFile
 
 
 class ModuleDetails(object):
     """Represents the details of the module."""
 
     def getDoc(self):
-        """Get the doc string of the module STX formatted.
-
-        Example::
-
-          The class we are using for this view is zope.app.apidoc.classmodule.
-
-          >>> from tests import getModuleDetailsView
-          >>> view = getModuleDetailsView()
-
-          >>> print view.getDoc().strip()
-          <div class="document">
-          <p>Class Documentation Module</p>
-          <p>This module is able to take a dotted name of any class and display
-          documentation for it.</p>
-          </div>
-        """
+        """Get the doc string of the module STX formatted."""
         text = self.context.getDocString()
         if text is None:
             return None
@@ -60,32 +45,7 @@
         return renderText('\n'.join(lines), self.context.getPath())
 
     def getEntries(self, columns=True):
-        """Return info objects for all modules and classes in this module.
-
-        Example::
-
-          The class we are using for this view is zope.app.apidoc.classmodule.
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getModuleDetailsView
-          >>> view = getModuleDetailsView()
-
-          >>> entries = view.getEntries(False)
-          >>> entries.sort(lambda x, y: cmp(x['name'], y['name']))
-          >>> pprint(entries[6:8])
-          [[('isclass', False),
-            ('isfunction', False),
-            ('ismodule', True),
-            ('iszcmlfile', False),
-            ('name', 'browser'),
-            ('url', 'http://127.0.0.1/zope/app/apidoc/classmodule/browser')],
-           [('isclass', False),
-            ('isfunction', True),
-            ('ismodule', False),
-            ('iszcmlfile', False),
-            ('name', 'cleanUp'),
-            ('url', 'http://127.0.0.1/zope/app/apidoc/classmodule/cleanUp')]]
-        """
+        """Return info objects for all modules and classes in this module."""
         entries = [{'name': name,
                     'url': zapi.absoluteURL(obj, self.request),
                     'ismodule': zapi.isinstance(obj, Module),
@@ -94,7 +54,7 @@
                     'isclass': zapi.isinstance(obj, Class),
                     'isfunction': zapi.isinstance(obj, Function),
                     'istextfile': zapi.isinstance(obj, TextFile),
-                    'iszcmlfile': zapi.isinstance(obj, Configuration)}
+                    'iszcmlfile': zapi.isinstance(obj, ZCMLFile)}
                    for name, obj in self.context.items()]
         entries.sort(lambda x, y: cmp(x['name'], y['name']))
         if columns:
@@ -105,23 +65,7 @@
         """Create breadcrumbs for the module path.
 
         We cannot reuse the the system's bread crumbs, since they go all the
-        way up to the root, but we just want to go to the root module.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getModuleDetailsView
-          >>> view = getModuleDetailsView()
-
-          >>> crumbs = [crumb.items() for crumb in view.getBreadCrumbs()]
-          >>> pprint(crumbs)
-          [[('url', 'http://127.0.0.1'), ('name', u'[top]')],
-           [('url', 'http://127.0.0.1/zope'), ('name', u'zope')],
-           [('url', 'http://127.0.0.1/zope/app'), ('name', 'app')],
-           [('url', 'http://127.0.0.1/zope/app/apidoc'), ('name', 'apidoc')],
-           [('url', 'http://127.0.0.1/zope/app/apidoc/classmodule'),
-            ('name', 'classmodule')]]
-        """
+        way up to the root, but we just want to go to the root module."""
         names = self.context.getPath().split('.') 
         crumbs = []
         module = self.context

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/tests.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/tests.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,108 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Tests for the Code Documentation Module
+
+$Id: tests.py 29269 2005-02-23 22:22:48Z srichter $
+"""
+import os
+import unittest
+from zope.component.interfaces import IFactory
+from zope.configuration import xmlconfig
+from zope.interface import directlyProvides, implements
+from zope.testing import doctest, doctestunit
+
+import zope.app.appsetup.appsetup
+from zope.app.renderer.rest import ReStructuredTextSourceFactory
+from zope.app.renderer.rest import IReStructuredTextSource
+from zope.app.renderer.rest import ReStructuredTextToHTMLRenderer
+from zope.app.testing import placelesssetup, setup, ztapi
+from zope.app.traversing.interfaces import IContainmentRoot
+
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
+from zope.app.apidoc.codemodule.codemodule import CodeModule
+from zope.app.apidoc.zcmlmodule import ZCMLModule
+
+# Just for loading purposes
+import zope.app.apidoc.codemodule.browser.module
+import zope.app.apidoc.codemodule.browser.class_
+import zope.app.apidoc.codemodule.browser.function
+import zope.app.apidoc.codemodule.browser.text
+import zope.app.apidoc.codemodule.browser.zcml
+
+def foo(cls, bar=1, *args):
+    """This is the foo function."""
+foo.deprecated = True
+
+def setUp(test):
+    placelesssetup.setUp()
+    setup.setUpTraversal()
+
+    class RootModule(str):
+        implements(IAPIDocRootModule)
+    ztapi.provideUtility(IAPIDocRootModule, RootModule('zope'), "zope")
+
+    module = CodeModule()
+    module.__name__ = ''
+    directlyProvides(module, IContainmentRoot)
+    ztapi.provideUtility(IDocumentationModule, module, "Code")
+
+    module = ZCMLModule()
+    module.__name__ = ''
+    directlyProvides(module, IContainmentRoot)
+    ztapi.provideUtility(IDocumentationModule, module, "ZCML")
+
+    # Register Renderer Components
+    ztapi.provideUtility(IFactory, ReStructuredTextSourceFactory,
+                         'zope.source.rest')    
+    ztapi.browserView(IReStructuredTextSource, '', 
+                      ReStructuredTextToHTMLRenderer)
+    # Cheat and register the ReST factory for STX as well.
+    ztapi.provideUtility(IFactory, ReStructuredTextSourceFactory,
+                         'zope.source.stx')
+
+    meta = os.path.join(os.path.dirname(zope.app.__file__), 'meta.zcml')
+    context = xmlconfig.file(meta, zope.app)
+    meta = os.path.join(os.path.dirname(zope.app.apidoc.__file__), 'meta.zcml')
+    context = xmlconfig.file(meta, zope.app.apidoc, context)
+
+    # Fix up path for tests.
+    global old_context
+    old_context = zope.app.appsetup.appsetup.__config_context
+    zope.app.appsetup.appsetup.__config_context = context
+
+    # Fix up path for tests.
+    global old_source_file
+    old_source_file = zope.app.appsetup.appsetup.__config_source
+    zope.app.appsetup.appsetup.__config_source = os.path.join(
+        os.path.dirname(zope.app.__file__), 'meta.zcml')
+
+
+def tearDown(test):
+    placelesssetup.tearDown()
+    global old_context, old_source_file
+    zope.app.appsetup.appsetup.__config_context = old_context    
+    zope.app.appsetup.appsetup.__config_source = old_source_file    
+
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp, tearDown=tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(default="test_suite")


Property changes on: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/tests.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -22,6 +22,7 @@
     """Represents the details of the text file."""
 
     def renderedContent(self):
+        """Render the file content to HTML."""
         if self.context.path.endswith('.stx'):
             format = 'zope.source.stx'
         else:

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -16,8 +16,9 @@
 $Id$
 """
 __docformat__ = "reStructuredText"
-from zope.configuration.fields import GlobalObject, GlobalInterface
+from zope.configuration.fields import GlobalObject, GlobalInterface, Tokens
 from zope.interface import implements
+from zope.interface.interfaces import IInterface
 from zope.schema import getFieldNamesInOrder, getFieldsInOrder
 from zope.schema.interfaces import IFromUnicode
 from zope.security.proxy import removeSecurityProxy
@@ -27,6 +28,8 @@
 from zope.app.apidoc.interfaces import IDocumentationModule
 from zope.app.apidoc.utilities import getPythonPath
 
+from zope.app.apidoc.codemodule.interfaces import IRootDirective
+
 def findDocModule(obj):
     if IDocumentationModule.providedBy(obj):
         return obj
@@ -44,83 +47,117 @@
         valueY = 999999
 
     return cmp(valueX, valueY)
-        
 
-class DisplayComment(object):
 
-    value = property(lambda self: self.context.value)
+class DirectiveDetails(object):
 
-    id = property(lambda self: IUniqueId(self.context).getId())
+    def fullTagName(self):
+        context = removeSecurityProxy(self.context)
+        ns, name = context.name
+        if context.prefixes[ns]:
+            return '%s:%s' %(context.prefixes[ns], name)
+        else:
+            return name
 
+    def line(self):
+        return str(removeSecurityProxy(self.context).info.line)
 
-class DisplayDirective(object):
+    def highlight(self):
+        if self.request.get('line') == self.line():
+            return 'highlight'
+        return ''
 
-    id = property(lambda self: IUniqueId(self.context).getId())
-
-    fullTagName = property(lambda self: self.context.getFullTagName())
-
     def url(self):
-        # XXX: Determine URLs of directives that are in all namespaces
         context = removeSecurityProxy(self.context)
-        ns = context.domElement.namespaceURI
+        ns, name = context.name
         ns = ns.replace(':', '_co_')
         ns = ns.replace('/', '_sl_')
         zcml = zapi.getUtility(IDocumentationModule, 'ZCML')
+        if name not in zcml[ns]:
+            ns = 'ALL'
         return '%s/../ZCML/%s/%s/index.html' %(
-            zapi.absoluteURL(findDocModule(self), self.request), ns,
-            context.domElement.localName)
+            zapi.absoluteURL(findDocModule(self), self.request), ns, name)
         
+    def ifaceURL(self, value, field, rootURL):
+        bound = field.bind(self.context.context)
+        iface = bound.fromUnicode(value)
+        return rootURL+'/../Interface/%s/apiindex.html' %(getPythonPath(iface))
+  
+    def objectURL(self, value, field, rootURL):
+        bound = field.bind(self.context.context)
+        obj = bound.fromUnicode(value)
+        if IInterface.providedBy(obj):
+            return rootURL+'/../Interface/%s/apiindex.html' %(
+                getPythonPath(obj))
+        try:
+            return rootURL + '/%s/index.html' %(
+                getPythonPath(obj).replace('.', '/'))
+        except AttributeError:
+            # probably an instance
+            pass
 
     def attributes(self):
-        schema = removeSecurityProxy(self.context.schema)
-        resolver = self.context.config.getResolver()
-        attrs = [{'name': name, 'value': value, 'url': None}
-                 for name, value in self.context.getAttributeMap().items()]
+        context = removeSecurityProxy(self.context)
+        attrs = [{'name': (ns and context.prefixes[ns]+':' or '') + name,
+                  'value': value, 'url': None, 'values': []}
+                 for (ns, name), value in context.attrs.items()]
 
+        names = context.schema.names(True)
+        rootURL = zapi.absoluteURL(findDocModule(self), self.request)
         for attr in attrs:
-            if name in schema:
-                field = schema[name]
-            elif name+'_' in schema:
-                field = schema[name+'_']
-            else:
-                continue
+            name = (attr['name'] in names) and attr['name'] or attr['name']+'_'
+            field = context.schema.get(name)
+            if zapi.isinstance(field, GlobalInterface):
+                attr['url'] = self.ifaceURL(attr['value'], field, rootURL)
+                
+            elif zapi.isinstance(field, GlobalObject):
+                attr['url'] = self.objectURL(attr['value'], field, rootURL)
 
-            # XXX: This is extremly brittle!!!
-            # Handle tokens; handle instances
-            if isinstance(field, GlobalInterface):
-                bound = field.bind(resolver)
-                converter = IFromUnicode(bound)
-                try:
-                    value = converter.fromUnicode(attr['value'])
-                except: continue
-                attr['url'] = '%s/../Interface/%s/apiindex.html' %(
-                    zapi.absoluteURL(findDocModule(self), self.request),
-                    getPythonPath(value))
+            elif zapi.isinstance(field, Tokens):
+                field = field.value_type
+                values = attr['value'].strip().split()
+                if len(values) == 1:
+                    attr['value'] = values[0]
+                    if zapi.isinstance(field, GlobalInterface):
+                        attr['url'] = self.ifaceURL(values[0], field, rootURL)
+                    elif zapi.isinstance(field, GlobalObject):
+                        attr['url'] = self.objectURL(values[0], field, rootURL)
+                    break
+                    
+                for value in values: 
+                    if zapi.isinstance(field, GlobalInterface):
+                        url = self.ifaceURL(value, field, rootURL)
+                    elif zapi.isinstance(field, GlobalObject):
+                        url = self.objectURL(value, field, rootURL)
+                    else:
+                        break
+                    attr['values'].append({'value': value, 'url': url})
+                    
 
-            elif isinstance(field, GlobalObject):
-                bound = field.bind(resolver)
-                converter = IFromUnicode(bound)
-                # XXX: Fix later
-                try:
-                    value = converter.fromUnicode(attr['value'])
-                except:
-                    pass
-                try:
-                    attr['url'] = getPythonPath(value)                
-                except AttributeError: continue
-
         # Make sure that the attributes are in the same order they are defined
         # in the schema.
-        fieldNames = getFieldNamesInOrder(schema)
+        fieldNames = getFieldNamesInOrder(context.schema)
         fieldNames = [name.endswith('_') and name[:-1] or name
                       for name in fieldNames]
         attrs.sort(lambda x, y: _compareAttrs(x, y, fieldNames))
 
-        return attrs
+        if not IRootDirective.providedBy(context):
+            return attrs
 
+        xmlns = []
+        for uri, prefix in context.prefixes.items():
+            name = prefix and ':'+prefix or ''
+            xmlns.append({'name': 'xmlns'+name,
+                          'value': uri,
+                          'url': None,
+                          'values': []})
+
+        xmlns.sort(lambda x, y: cmp(x['name'], y['name']))
+        return xmlns + attrs
+
     def hasSubDirectives(self):
-        return len(self.context) != 0
+        return len(removeSecurityProxy(self.context).subs) != 0
 
     def getElements(self):
         context = removeSecurityProxy(self.context)
-        return context.values()
+        return context.subs

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -37,11 +37,16 @@
   font-style: italic;
 }
 
+div.highlight {
+  background: #fffbbe;
+  border: 1pt solid #0000c0;
+}
+
   </style>
 </head>
 <body>
 
-  <div tal:replace="structure context/@@display" />
+  <div tal:replace="structure context/rootElement/@@display" />
 
 </body>
 </html>
\ No newline at end of file

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -29,53 +29,7 @@
 from interfaces import IClassDocumentation
 
 class Class(object):
-    """This class represents a class declared in the module.
-
-    Setting up a class for documentation is easy. You only need to provide an
-    object providing 'IModule' as a parent, the name and the klass itself::
-
-      >>> import zope.app.apidoc
-      >>> module = Module(None, 'apidoc', zope.app.apidoc)
-      >>> klass = Class(module, 'APIDocumentation',
-      ...               zope.app.apidoc.APIDocumentation)
-
-    This class provides data about the class in an accessible format. The
-    Python path and doc string are easily retrieved using::
-
-      >>> klass.getPath()
-      'zope.app.apidoc.APIDocumentation'
-
-      >>> klass.getDocString()[:41]
-      'Represent the complete API Documentation.'
-
-    A list of base classes can also be retrieved. The list only includes
-    direct bases, so if we have class 'Blah', which extends 'Bar', which
-    extends 'Foo', then the bases of 'Blah' is just 'Bar'. In our example this
-    looks like this::
-
-      >>> klass.getBases()
-      (<class 'zope.app.apidoc.utilities.ReadContainerBase'>,)
-
-    In the other direction, you can get a list of known subclasses.  The list
-    only includes those subclasses that are registered with the global
-    classRegistry in this module. In our example::
-
-      >>> class APIDocSubclass(zope.app.apidoc.APIDocumentation):
-      ...   pass
-      >>> klass2 = Class(module, 'APIDocSubclass', APIDocSubclass)
-      >>> klass.getKnownSubclasses()
-      [<class 'zope.app.apidoc.classmodule.APIDocSubclass'>]
-
-    For a more detailed analysis, you can also retrieve the public attributes
-    and methods of this class::
-
-      >>> klass.getAttributes()
-      []
-
-      >>> klass.getMethods()[0]
-      ('get', <unbound method APIDocumentation.get>, None)
-
-    """
+    """This class represents a class declared in the module."""
     implements(ILocation, IClassDocumentation)
 
     def __init__(self, module, name, klass):
@@ -118,31 +72,7 @@
         return self.__interfaces
 
     def getAttributes(self):
-        """See IClassDocumentation.
-
-        Here a detailed example::
-
-          >>> from zope.app.apidoc.tests import pprint
-
-          >>> class ModuleStub(object):
-          ...      def getPath(self): return ''
-
-          >>> class IBlah(Interface):
-          ...      foo = Attribute('Foo')
-
-          >>> class Blah(object):
-          ...      implements(IBlah)
-          ...      foo = 'f'
-          ...      bar = 'b'
-          ...      _blah = 'l'
-
-          >>> klass = Class(ModuleStub(), 'Blah', Blah)
-
-          >>> attrs = klass.getAttributes()
-          >>> pprint(attrs)
-          [('bar', 'b', None),
-           ('foo', 'f', <InterfaceClass zope.app.apidoc.classmodule.IBlah>)]
-        """
+        """See IClassDocumentation."""
         return [
             (name, getattr(self.__klass, name),
              getInterfaceForAttribute(name, self.__all_ifaces, asPath=False))
@@ -151,33 +81,7 @@
             if not inspect.ismethod(getattr(self.__klass, name))]
 
     def getMethods(self):
-        """See IClassDocumentation.
-
-        Here a detailed example::
-
-          >>> from zope.app.apidoc.tests import pprint
-
-          >>> class ModuleStub(object):
-          ...      def getPath(self): return ''
-
-          >>> class IBlah(Interface):
-          ...      def foo(): pass
-
-          >>> class Blah(object):
-          ...      implements(IBlah)
-          ...      def foo(self): pass
-          ...      def bar(self): pass
-          ...      def _blah(self): pass
-
-          >>> klass = Class(ModuleStub(), 'Blah', Blah)
-
-          >>> methods = klass.getMethods()
-          >>> pprint(methods)
-          [('bar', <unbound method Blah.bar>, None),
-           ('foo',
-            <unbound method Blah.foo>,
-            <InterfaceClass zope.app.apidoc.classmodule.IBlah>)]
-        """
+        """See IClassDocumentation."""
         return [
             (name, getattr(self.__klass, name),
              getInterfaceForAttribute(name, self.__all_ifaces, asPath=False))

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/codemodule.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/codemodule.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/codemodule.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,95 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Code Documentation Module
+
+This module is able to take a dotted name of any class and display
+documentation for it.
+
+$Id: __init__.py 29269 2005-02-23 22:22:48Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.interface import Interface, implements
+from zope.app import zapi
+from zope.app.i18n import ZopeMessageIDFactory as _
+from zope.app.location.interfaces import ILocation
+
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.classregistry import safe_import
+from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
+from zope.app.apidoc.codemodule.module import Module
+
+
+class CodeModule(Module):
+    """Represent the code browser documentation root"""
+    implements(IDocumentationModule)
+
+    # See zope.app.apidoc.interfaces.IDocumentationModule
+    title = _('Code Browser')
+
+    # See zope.app.apidoc.interfaces.IDocumentationModule
+    description = _("""
+    This module allows you to get an overview of the modules and classes
+    defined in the Zope 3 framework and its supporting packages. There are
+    two methods to navigate through the modules to find the classes you are
+    interested in.
+
+    The first method is to type in some part of the Python path of the class
+    and the module will look in the class registry for matches. The menu will
+    then return with a list of these matches.
+
+    The second method is to click on the "Browse Zope Source" link. In the
+    main window, you will see a directory listing with the root Zope 3
+    modules. You can click on the module names to discover their content. If a
+    class is found, it is represented as a bold entry in the list.
+
+    The documentation contents of a class provides you with an incredible
+    amount of information. Not only does it tell you about its base classes,
+    implemented interfaces, attributes and methods, but it also lists the
+    interface that requires a method or attribute to be implemented and the
+    permissions required to access it.
+    """)
+    def __init__(self):
+        """Initialize object."""
+        super(CodeModule, self).__init__(None, '', None, False)
+        self.__isSetup = False
+
+    def setup(self):
+        """Setup module and class tree."""
+        if self.__isSetup:
+            return
+        for name, mod in zapi.getUtilitiesFor(IAPIDocRootModule):
+            self._children[name] = Module(self, name, safe_import(mod))
+        self.__isSetup = True
+
+    def getDocString(self):
+        """See Module class."""
+        return _('Zope 3 root.')
+
+    def getFileName(self):
+        """See Module class."""
+        return ''
+
+    def getPath(self):
+        """See Module class."""
+        return ''
+
+    def get(self, key, default=None):
+        """See zope.app.container.interfaces.IReadContainer."""
+        self.setup()
+        return super(CodeModule, self).get(key, default)
+
+    def items(self):
+        """See zope.app.container.interfaces.IReadContainer."""
+        self.setup()
+        return super(CodeModule, self).items()


Property changes on: Zope3/trunk/src/zope/app/apidoc/codemodule/codemodule.py
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml	2005-02-26 14:16:04 UTC (rev 29309)
@@ -15,15 +15,19 @@
     <allow interface=".interfaces.IFunctionDocumentation" />
   </class>
 
-  <class class=".zcml.Configuration">
-    <allow interface=".interfaces.IConfiguration" />
+  <class class=".zcml.Directive">
+    <allow interface=".interfaces.IDirective" />
   </class>
 
+  <class class=".zcml.ZCMLFile">
+    <allow interface=".interfaces.IZCMLFile" />
+  </class>
+
   <class class=".text.TextFile">
     <allow attributes="getContent path" />
   </class>
 
-  <class class=".CodeModule">
+  <class class=".codemodule.CodeModule">
     <allow interface="..interfaces.IDocumentationModule" />
     <allow interface=".interfaces.IModuleDocumentation" />
     <allow attributes="rootModules" />
@@ -41,7 +45,7 @@
 
   <utility
     provides="..interfaces.IDocumentationModule"
-    factory=".CodeModule"
+    factory=".codemodule.CodeModule"
     name="Code" />
 
   <include package=".browser" />

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -0,0 +1,34 @@
+=================================
+The `apidoc:rootModule` Directive
+=================================
+
+The `rootModule` directive allows you to register a third party Python package
+with apidoc's code browser. 
+
+Before we can register a new root module, we need to load the
+metaconfiguration:
+
+  >>> from zope.configuration import xmlconfig
+  >>> import zope.app.apidoc.codemodule
+  >>> context = xmlconfig.file('meta.zcml', zope.app.apidoc.codemodule)
+
+Now we can run the directive. First, let's make sure that no root modules have
+been registered yet:
+
+  >>> from zope.app import zapi
+  >>> from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
+  >>> list(zapi.getUtilitiesFor(IAPIDocRootModule))
+  []
+
+Now run the registration code:
+
+  >>> context = xmlconfig.string('''
+  ...     <configure
+  ...         xmlns='http://namespaces.zope.org/apidoc'>
+  ...       <rootModule module="zope" />
+  ...     </configure>''', context)
+
+and the root module is available:
+
+  >>> list(zapi.getUtilitiesFor(IAPIDocRootModule))
+  [(u'zope', 'zope')]


Property changes on: Zope3/trunk/src/zope/app/apidoc/codemodule/directive.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -1,91 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003, 2004 Zope Corporation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Functional Tests for Class Documentation Module.
-
-$Id$
-"""
-import unittest
-from zope.app.testing.functional import BrowserTestCase
-
-class ClassModuleTests(BrowserTestCase):
-    """Just a couple of tests ensuring that the templates render."""
-
-    def testMenu(self):
-        response = self.publish('/++apidoc++/Code/menu.html', 
-                                basic='mgr:mgrpw')
-        self.assertEqual(response.getStatus(), 200)
-        body = response.getBody()
-        self.assert_(body.find('Zope Source') > 0)
-        self.checkForBrokenLinks(body, '/++apidoc++/Code/menu.html',
-                                 basic='mgr:mgrpw')
-
-    def testMenuClassFinder(self):
-        response = self.publish('/++apidoc++/Code/menu.html',
-                                basic='mgr:mgrpw',
-                                form={'path': 'Code', 'SUBMIT': 'Find'})
-        self.assertEqual(response.getStatus(), 200)
-        body = response.getBody()
-        self.assert_(body.find('zope.app.apidoc.codemodule.CodeModule') > 0)
-        self.checkForBrokenLinks(body, '/++apidoc++/Code/menu.html',
-                                 basic='mgr:mgrpw')
-
-    def testModuleDetailsView(self):
-        response = self.publish('/++apidoc++/Code/zope/app/apidoc/apidoc',
-                                basic='mgr:mgrpw')
-        self.assertEqual(response.getStatus(), 200)
-        body = response.getBody()
-        self.assert_(body.find('Zope 3 API Documentation') > 0)
-        self.checkForBrokenLinks(
-            body, '/++apidoc++/Code/zope/app/apidoc/apidoc', basic='mgr:mgrpw')
-
-    def testClassDetailsView(self):
-        response = self.publish(
-            '/++apidoc++/Code/zope/app/apidoc/apidoc/APIDocumentation',
-            basic='mgr:mgrpw')
-        self.assertEqual(response.getStatus(), 200)
-        body = response.getBody()
-        self.assert_(body.find('Represent the complete API Documentation.') > 0)
-        self.checkForBrokenLinks(
-            body, '/++apidoc++/Code/zope/app/apidoc/apidoc/APIDocumentation',
-            basic='mgr:mgrpw')
-
-    def testFunctionDetailsView(self):
-        response = self.publish(
-            '/++apidoc++/Code/zope/app/apidoc/apidoc/handleNamespace',
-            basic='mgr:mgrpw')
-        self.assertEqual(response.getStatus(), 200)
-        body = response.getBody()
-        self.assert_(body.find('handleNamespace(ob, name)') > 0)
-        self.checkForBrokenLinks(
-            body, '/++apidoc++/Code/zope/app/apidoc/apidoc/handleNamesapce',
-            basic='mgr:mgrpw')
-
-    def testZCMLFileDetailsView(self):
-        response = self.publish(
-            '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
-            basic='mgr:mgrpw')
-        self.assertEqual(response.getStatus(), 200)
-        body = response.getBody()
-        self.checkForBrokenLinks(
-            body, '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
-            basic='mgr:mgrpw')
-        
-
-def test_suite():
-    return unittest.TestSuite((
-        unittest.makeSuite(ClassModuleTests),
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/function.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/function.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/function.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -23,35 +23,7 @@
 from interfaces import IFunctionDocumentation
 
 class Function(object):
-    """This class represents a function declared in the module.
-
-    Setting up a function for documentation is easy. You only need to provide
-    an object providing 'IModule' as a parent, the name and the function
-    object itself::
-
-      >>> import zope.app.apidoc
-      >>> module = Module(None, 'apidoc', zope.app.apidoc)
-      >>> func = Function(module, 'handleNamespace',
-      ...                 zope.app.apidoc.handleNamespace)
-
-    This class provides data about the function in an accessible format. The
-    Python path, signature and doc string are easily retrieved using::
-
-      >>> func.getPath()
-      'zope.app.apidoc.handleNamespace'
-
-      >>> func.getSignature()
-      '(ob, name)'
-
-      >>> func.getDocString()
-      'Used to traverse to an API Documentation.'
-
-    For a more detailed analysis, you can also retrieve the attributes of the
-    function::
-
-      >>> func.getAttributes()
-      []
-    """
+    """This class represents a function declared in the module."""
     implements(ILocation, IFunctionDocumentation)
 
     def __init__(self, module, name, func):
@@ -72,27 +44,5 @@
         return getFunctionSignature(self.__func)
 
     def getAttributes(self):
-        """See IClassDocumentation.
-
-        Here a detailed example::
-
-          >>> class ModuleStub(object):
-          ...      def getPath(self): return ''
-
-          >>> def foo(bar=1):
-          ...     pass
-
-          >>> func = Function(ModuleStub(), 'foo', foo)
-
-          >>> attrs = func.getAttributes()
-          >>> attrs.sort()
-          >>> print attrs
-          []
-
-          >>> foo.bar = 'blah'
-          >>> attrs = func.getAttributes()
-          >>> attrs.sort()
-          >>> print attrs
-          [('bar', 'blah')]
-        """
+        """See IClassDocumentation."""
         return self.__func.__dict__.items()

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -16,14 +16,22 @@
 $Id$
 """
 __docformat__ = "reStructuredText"
-from zope.interface import Interface
-from zope.schema import Field, BytesLine, Text
+import zope.interface
+import zope.schema
 
 from zope.app.container.interfaces import IContainer
 from zope.app.container.interfaces import IReadContainer
 from zope.app.i18n import ZopeMessageIDFactory as _
 
 
+class IAPIDocRootModule(zope.interface.Interface):
+    """Marker interface for utilities that represent class browser root
+    modules.
+
+    The utilities will be simple strings, representing the modules Python
+    dotted name.
+    """ 
+
 class IModuleDocumentation(IReadContainer):
     """Representation of a Python module for documentation.
 
@@ -39,7 +47,7 @@
         """Return the Python path of the module."""
 
 
-class IClassDocumentation(Interface):
+class IClassDocumentation(zope.interface.Interface):
     """Representation of a class or type for documentation."""
 
     def getDocString():
@@ -88,7 +96,7 @@
         """
 
 
-class IFunctionDocumentation(Interface):
+class IFunctionDocumentation(zope.interface.Interface):
     """Representation of a function for documentation."""
 
     def getDocString():
@@ -107,109 +115,67 @@
         second is the attribute object itself.
         """
 
+class IDirective(zope.interface.Interface):
+    """Representation of a directive in IZCMLFile."""
 
-class IElement(IContainer):
-    """Represents an XML Element in the ZCML Configuration File
+    name = zope.schema.Tuple(
+        title=u'Name',
+        description=u'Name of the directive in the form (Namespace. Name)',
+        required = True)
+    
+    schema = zope.schema.Field(
+        title=u'Schema',
+        description=u'Schema describing the directive attributes',
+        required = True)
 
-    The names of the container items is simply their index (as string).
-    """
-
-    domElement = Field(
-        title=_("Mini-DOM Element"),
-        description=_("Mini-DOM element representing this configuration "
-                      "element."),
-        required=True)
-
-    def toXML():
-        """Returns a string completely representing this DOM Element.
+    attrs = zope.schema.Field(
+        title=u'Attributes',
+        description=u'SAX parser representation of the directive\'s attributes',
+        required = True)
         
-        The returned XML should be well formatted, including all correct
-        indentations and lines not being long than 80 characters, if possible.
-        """
+    context = zope.schema.Field(
+        title=u'Configuration Context',
+        description=u'Configuration context while the directive was parsed.',
+        required = True)
 
-    def getElementType():
-        """Return the type of the DOM element.
-        
-        Possible values are:
+    prefixes = zope.schema.Dict(
+        title=u'Prefixes',
+        description=u'Mapping from namespace URIs to prefixes.',
+        required = True)
 
-        - ELEMENT_NODE (1): An XML tag. If an element is of this type it is a
-          standard ZCML directive or sub-directive.
+    info = zope.schema.Field(
+        title=u'Info',
+        description=u'ParserInfo objects containing line and column info.',
+        required = True)
 
-        - COMMENT_NODE (8): An XML comment. Comments are used to explain
-          features of the ZCML directives and are thus supported by the editor.
+    __parent__ = zope.schema.Field(
+        title=u'Parent',
+        description=u'Parent Directive',
+        required = True)
 
-        - TEXT_NODE (3): A simple text node. These are commonly ignored by te
-          configuration editor, since they are arbitrary and ZCML does not make
-          use of text nodes ever. Thus, an element having this type would be
-          considered a bug.
-        """
+    subs = zope.schema.List(
+        title=u'Sub-Directives',
+        description=u'List of sub-directives',
+        required = True)
 
-    def isFirstChild():
-        """Determine whether this element is the first child in its parent."""
 
-    def isLastChild():
-        """Determine whether this element is the last child in its parent."""
+class IRootDirective(IDirective):
+    """Marker interface"""
 
-    def validate():
-        """Validate the element's data.
 
-        If the validation is successful, `None` should be returned; otherwise
-        return a `ValidationError` or a list of validation errors.
+class IZCMLFile(zope.interface.Interface):
+    """ZCML File Object
 
-        While a DOM element always represents a valid XML expression, this might
-        not be true for ZCML, since it is just a subset of XML.
-        """
-
-
-class IComment(IElement):
-    """A special element representing a comment in the configuration."""
-
-    value = Text(
-        title=_("Comment"),
-        description=_("This is the actual comment text. It is automatically "
-                      "extracted from the DOM element."),
-        required=True)
-
-
-class IDirective(IElement):
-    """
-
-    The element will also expose the attributes of the XML element as attributes
-    of this class.
-    """
-    config = Field()
-
-    schema = Field()
-
-    def getFullTagName():
-        """ """
-
-    def getAttribute(name):
-        """ """
-
-    def getAttributeMap():
-        """ """
-
-    def removeAttribute(name):
-        """ """
-
-    def getAvailableSubdirectives():
-        """ """
-        
-
-class IConfiguration(IDirective):
-    """ZCML Configuration Object
-
     This is the main object that will manage the configuration of one particular
     ZCML configuration file.
     """
 
-    filename = BytesLine(
+    filename = zope.schema.BytesLine(
         title=_('Configuration Filename'),
         description=_('Path to the configuration file'),
         required=True)
 
-    package = BytesLine(
+    package = zope.schema.BytesLine(
         title=_('Configuration Package'),
         description=_(
         '''Specifies the package from which the configuration file will be
@@ -217,24 +183,8 @@
         cannot be fully validated and improper ZCML files might be written.'''),
         required=False)
 
-    def parse():
-        """Parse the ZCML located in the specified file.
-
-        If the file does not exist, a configuration will be initialized.
-        """
-
-    def getSchema(namespaceURI, tagName):
-        """Given the namespace and the tag name, lookup the directive's
-        schema.
-
-        If no schema is found, `None` is returned.
-        """
-
-    def getNamespacePrefix(namespaceURI):
-        """ """
-
-    def getNamespaceURI(prefix):
-        """ """
-
-    def getResolver():
-        """ """
+    rootElement = zope.schema.Field(
+        title=_("XML Root Element"),
+        description=_("XML element representing the configuration root."),
+        required=True)
+    

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -18,7 +18,7 @@
 __docformat__ = 'restructuredtext'
 from zope.interface import implements
 from zope.app.component.metaconfigure import utility
-from zope.app.apidoc.codemodule import IAPIDocRootModule
+from zope.app.apidoc.codemodule.interfaces import IAPIDocRootModule
 
 class RootModule(str):
     implements(IAPIDocRootModule)

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/module.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/module.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/module.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -23,7 +23,7 @@
 from zope.interface import implements
 from zope.interface.interface import InterfaceClass
 from zope.app.location.interfaces import ILocation
-from zope.app.location import locate
+from zope.app.location import LocationProxy
 
 from zope.app.apidoc.classregistry import safe_import
 from zope.app.apidoc.utilities import ReadContainerBase
@@ -32,70 +32,15 @@
 from zope.app.apidoc.codemodule.class_ import Class
 from zope.app.apidoc.codemodule.function import Function
 from zope.app.apidoc.codemodule.text import TextFile
-from zope.app.apidoc.codemodule.zcml import Configuration
+from zope.app.apidoc.codemodule.zcml import ZCMLFile
 
 # Ignore these files, since they are not necessary or cannot be imported
 # correctly.
-# TODO: I want to be able to specify paths with wildcards later, so that we do
-# not ignore all files/dirs with a certain name.
 IGNORE_FILES = ('tests', 'tests.py', 'ftests', 'ftests.py', 'CVS', 'gadfly',
                 'setup.py', 'introspection.py', 'Mount.py')
 
 class Module(ReadContainerBase):
-    """This class represents a Python module.
-
-    The module can be easily setup by simply passing the parent module, the
-    module name (not the entire Python path) and the Python module instance
-    itself::
-
-      >>> import zope.app.apidoc
-      >>> module = Module(None, 'apidoc', zope.app.apidoc)
-
-    We can now get some of the common module attributes via accessor methods::
-
-      >>> module.getDocString()[:24]
-      'Zope 3 API Documentation'
-
-      >>> fname = module.getFileName()
-      >>> fname = fname.replace('\\\\', '/') # normalize pathname separator
-      >>> 'zope/app/apidoc/__init__.py' in fname
-      True
-
-      >>> module.getPath()
-      'zope.app.apidoc'
-
-    The setup for creating the sub module and class tree is automatically
-    called during initialization, so that the sub-objects are available as
-    soon as you have the object::
-
-      >>> keys = module.keys()
-      >>> 'APIDocumentation' in keys
-      True
-      >>> 'apidocNamespace' in keys
-      True
-      >>> 'handleNamespace' in keys
-      True
-
-      >>> print module['browser'].getPath()
-      zope.app.apidoc.browser
-
-    Now, the ``get(key, default=None)`` is actually much smarter than you might
-    originally suspect, since it can actually get to more objects than it
-    promises. If a key is not found in the module's children, it tries to
-    import the key as a module relative to this module.
-
-    For example, while 'tests' directories are not added to the module and
-    classes hierarchy (since they do not provide or implement any API), we can
-    still get to them::
-
-      >>> print module['tests'].getPath()
-      zope.app.apidoc.tests
-
-      >>> names = module['tests'].keys()
-      >>> names.sort()
-      >>> names
-      ['Root', 'pprint', 'rootLocation', 'setUp', 'test_suite']
-    """
+    """This class represents a Python module."""
     implements(ILocation, IModuleDocumentation)
 
     def __init__(self, parent, name, module, setup=True):
@@ -135,8 +80,8 @@
                         self._children[name] = Module(self, name, module)
 
                 elif os.path.isfile(path) and file.endswith('.zcml'):
-                    self._children[file] = Configuration(path, self._module,
-                                                         self, file)
+                    self._children[file] = ZCMLFile(path, self._module,
+                                                    self, file)
 
                 elif os.path.isfile(path) and file.endswith('.txt'):
                     self._children[file] = TextFile(path, file, self)
@@ -157,8 +102,8 @@
                 if isinstance(attr, (types.ClassType, types.TypeType)):
                     self._children[name] = Class(self, name, attr)
 
-                if isinstance(attr, (InterfaceClass)):
-                    self._children[name] = locate(attr, self, name)
+                if isinstance(attr, InterfaceClass):
+                    self._children[name] = LocationProxy(attr, self, name)
 
                 elif type(attr) is types.FunctionType:
                     self._children[name] = Function(self, name, attr)

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -15,132 +15,44 @@
 
 $Id$
 """
+import os
 import unittest
-from zope.component.interfaces import IFactory
 from zope.configuration import xmlconfig
-from zope.interface import Interface, directlyProvides, implements
-from zope.publisher.browser import TestRequest
-from zope.testing.doctestunit import DocTestSuite
+from zope.testing import doctest, doctestunit
 
-from zope.app import zapi
-from zope.app.location.traversing import LocationPhysicallyLocatable
-from zope.app.renderer.rest import ReStructuredTextSourceFactory
-from zope.app.renderer.rest import IReStructuredTextSource
-from zope.app.renderer.rest import ReStructuredTextToHTMLRenderer
-from zope.app.renderer.stx import StructuredTextSourceFactory
-from zope.app.renderer.stx import IStructuredTextSource
-from zope.app.renderer.stx import StructuredTextToHTMLRenderer
-from zope.app.testing import placelesssetup, ztapi
-from zope.app.traversing.browser import AbsoluteURL, SiteAbsoluteURL
-from zope.app.traversing.interfaces import ITraversable, ITraverser
-from zope.app.traversing.interfaces import IPhysicallyLocatable
-from zope.app.traversing.interfaces import IContainmentRoot
-from zope.app.traversing.adapters import DefaultTraversable
-from zope.app.traversing.adapters import RootPhysicallyLocatable
-from zope.app.traversing.adapters import Traverser
+import zope.app.appsetup.appsetup
+from zope.app.testing import placelesssetup
 
-from zope.app.apidoc.codemodule import CodeModule, IAPIDocRootModule
-from zope.app.apidoc.codemodule.browser.class_ import ClassDetails
-from zope.app.apidoc.codemodule.browser.function import FunctionDetails
-from zope.app.apidoc.codemodule.browser.module import ModuleDetails
-from zope.app.apidoc.interfaces import IDocumentationModule
 
-
 def setUp(test):
     placelesssetup.setUp()
 
-    class RootModule(str):
-        implements(IAPIDocRootModule)
-    ztapi.provideUtility(IAPIDocRootModule, RootModule('zope'), "zope")
+    meta = os.path.join(os.path.dirname(zope.app.__file__), 'meta.zcml')
+    context = xmlconfig.file(meta, zope.app)
+    meta = os.path.join(os.path.dirname(zope.app.apidoc.__file__), 'meta.zcml')
+    context = xmlconfig.file(meta, zope.app.apidoc, context)
 
-    module = CodeModule()
-    module.__name__ = ''
-    directlyProvides(module, IContainmentRoot)
-    ztapi.provideUtility(IDocumentationModule, module, "Code")
+    # Fix up path for tests.
+    global old_context
+    old_context = zope.app.appsetup.appsetup.__config_context
+    zope.app.appsetup.appsetup.__config_context = context
 
-    ztapi.provideAdapter(
-        None, ITraverser, Traverser)
-    ztapi.provideAdapter(
-        None, ITraversable, DefaultTraversable)
-    ztapi.provideAdapter(
-        None, IPhysicallyLocatable, LocationPhysicallyLocatable)
-    ztapi.provideAdapter(
-        IContainmentRoot, IPhysicallyLocatable, RootPhysicallyLocatable)
+def tearDown(test):
+    placelesssetup.tearDown()
+    global old_context
+    zope.app.appsetup.appsetup.__config_context = old_context    
 
-    ztapi.browserView(Interface, "absolute_url", AbsoluteURL)
-    ztapi.browserView(IContainmentRoot, "absolute_url", SiteAbsoluteURL)
 
-    # Register Renderer Components
-    ztapi.provideUtility(IFactory, StructuredTextSourceFactory,
-                         'zope.source.stx')    
-    ztapi.provideUtility(IFactory, ReStructuredTextSourceFactory,
-                         'zope.source.rest')    
-    ztapi.browserView(IStructuredTextSource, '', 
-                      StructuredTextToHTMLRenderer)
-    ztapi.browserView(IReStructuredTextSource, '', 
-                      ReStructuredTextToHTMLRenderer)
-
-
-def foo(cls, bar=1, *args):
-    """This is the foo function."""
-foo.deprecated = True
-
-
-def getFunctionDetailsView():
-    cm = zapi.getUtility(IDocumentationModule, 'Code')
-    view = FunctionDetails()
-    view.context = zapi.traverse(cm, 'zope/app/apidoc/codemodule/tests/foo')
-    view.request = TestRequest()
-    return view
-
-
-def getClassDetailsView():
-    cm = zapi.getUtility(IDocumentationModule, 'Code')
-    view = CodeDetails()
-    view.context = zapi.traverse(cm, 'zope/app/apidoc/codemodule/CodeModule')
-    view.request = TestRequest()
-    return view
-
-
-def getModuleDetailsView():
-    cm = zapi.getUtility(IDocumentationModule, 'Code')
-    view = ModuleDetails()
-    view.context = zapi.traverse(cm, 'zope/app/apidoc/codemodule')
-    view.request = TestRequest()
-    return view
-
-
-class DirectivesTest(placelesssetup.PlacelessSetup, unittest.TestCase):
-
-    template = """
-        <configure
-            xmlns='http://namespaces.zope.org/apidoc'>
-          %s
-        </configure>"""
-    
-    def setUp(self):
-        super(DirectivesTest, self).setUp()
-        import zope.app.apidoc.codemodule
-        self.context = xmlconfig.file('meta.zcml', zope.app.apidoc.codemodule)
-
-    def testRootModule(self):
-        self.assertEqual(len(list(zapi.getUtilitiesFor(IAPIDocRootModule))), 0)
-        xmlconfig.string(
-            self.template %'<rootModule module="zope" />', self.context)
-
-        self.assertEqual(zapi.getUtilitiesFor(IAPIDocRootModule).next()[0],
-                         'zope')
-
-
 def test_suite():
     return unittest.TestSuite((
-        # XXX: Redo browser tests
-        #DocTestSuite('zope.app.apidoc.codemodule.browser',
-        #             setUp=setUp, tearDown=placelesssetup.tearDown),
-        DocTestSuite('zope.app.apidoc.codemodule',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
-        unittest.makeSuite(DirectivesTest),
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp, tearDown=tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('directive.txt',
+                             setUp=placelesssetup.setUp,
+                             tearDown=placelesssetup.tearDown),
         ))
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(default="test_suite")

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py	2005-02-26 14:16:04 UTC (rev 29309)
@@ -11,236 +11,125 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Configuration File Representation
+"""ZCML File Representation
 
 $Id$
 """
 __docformat__ = "reStructuredText"
+from xml.sax import make_parser
+from xml.sax.xmlreader import InputSource
+from xml.sax.handler import feature_namespaces
 
-from persistent import Persistent
-from xml.dom import minidom
-from xml.parsers.expat import ExpatError
-
 from zope.cachedescriptors.property import Lazy
-from zope.configuration import xmlconfig
-from zope.configuration.config import ConfigurationContext
-from zope.configuration.zopeconfigure import IZopeConfigure
-from zope.interface import implements
-from zope.schema import getFields
-from zope.schema.interfaces import IFromUnicode
+from zope.configuration import xmlconfig, config
+from zope.interface import implements, directlyProvides
 
 import zope.app.appsetup.appsetup
 from zope.app import zapi
-from zope.app.location import locate
-from zope.app.container.contained import Contained
 
-from interfaces import IElement, IComment, IDirective, IConfiguration
+from interfaces import IDirective, IRootDirective, IZCMLFile
 
-context = None
-def getContext():
-    global context
-    if context is None:
-        context = xmlconfig.file(zope.app.appsetup.appsetup.getConfigSource(),
-                                 execute=False)
-    return context
 
+class MyConfigHandler(xmlconfig.ConfigurationHandler, object):
+    """Special configuration handler to generate an XML tree."""
 
-class Element(Contained):
-    """A wrapper for a Mini-DOM Element to provide a Python/Zope-native
-    representation.
-    """
-    implements(IElement)
-    
-    def __init__(self, dom):
-        """Initialize the Element object."""
-        self.domElement = dom
+    def __init__(self, context):
+        super(MyConfigHandler, self).__init__(context)
+        self.rootElement = self.currentElement = None
+        self.prefixes = {}
 
-    def _getSubElement(self, dom, index):
-        """Helper method to create new element."""
-        element = Element(dom)
-        locate(element, self, unicode(index))
-        return element
+    def startPrefixMapping(self, prefix, uri):
+        self.prefixes[uri] = prefix
 
-    def get(self, key, default=None):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        try:
-            return self[key]
-        except KeyError:
-            return default
+    def startElementNS(self, name, qname, attrs):
+        # The last stack item is parent of the stack item that we are about to
+        # create
+        stackitem = self.context.stack[-1]
+        super(MyConfigHandler, self).startElementNS(name, qname, attrs)
 
-    def keys(self):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        dom = self.domElement
-        return [unicode(index) for index in range(len(dom.childNodes))
-                if dom.childNodes[index].nodeType != dom.TEXT_NODE]
+        # Get the parser info from the correct context
+        info = self.context.stack[-1].context.info
 
-    def values(self):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        return [self[key] for key in self.keys()]
+        # complex stack items behave a bit different than the other ones, so
+        # we need to handle it separately
+        if isinstance(stackitem, config.ComplexStackItem):
+            schema = stackitem.meta.get(name[1])[0]
+        else:
+            schema = stackitem.context.factory(stackitem.context, name).schema
 
-    def items(self):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        return [(key, self[key]) for key in self.keys()]
+        # Now we have all the necessary information to create the directive
+        element = Directive(name, schema, attrs, stackitem.context, info,
+                            self.prefixes)
+        # Now we place the directive into the XML directive tree.
+        if self.rootElement is None:
+            self.rootElement = element
+        else:
+            self.currentElement.subs.append(element)
+            
+        element.__parent__ = self.currentElement
+        self.currentElement = element
 
-    def __iter__(self):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        return iter(self.keys())
 
-    def __len__(self):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        return len(self.keys())
+    def endElementNS(self, name, qname):
+        super(MyConfigHandler, self).endElementNS(name, qname)
+        self.currentElement = self.currentElement.__parent__
 
-    def __contains__(self, key):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        try:
-            index = int(key)
-        except ValueError:
-            raise KeyError, '%s cannot be converted to an index.' %key
-        return index >= 0 and index < len(self.domElement.childNodes) and \
-               self.domElement.childNodes[index] != self.domElement.TEXT_NODE
 
-    def __getitem__(self, key):
-        """See zope.app.container.interfaces.IReadContainer"""        
-        try:
-            index = int(key)
-        except ValueError:
-            raise KeyError, '%s cannot be converted to an index.' %key
-        # Create the sub-element from the index and give it a location before
-        # returning it.
-        element = self._getSubElement(self.domElement.childNodes[index], index)
-        locate(element, self, key)
-        return element
-
-    def getElementType(self):
-        """See configeditor.interfaces.IElement"""
-        return self.domElement.nodeType
-
-
-class Comment(Element):
-    """ """
-    implements(IComment)
-
-    def getValue(self):
-        return self.domElement.nodeValue
-    value = property(getValue)
-
-
-class Directive(Element):
-    """ """
+class Directive(object):
+    """Representation of a ZCML directive."""
     implements(IDirective)
-    
-    def __init__(self, dom, tagName=None, namespaceURI=None,
-                 attributes=None, config=None):
-        self.domElement = dom
-        self.config = config
-        # Delay lookup till later
-        self.schema = None
-
-    def _getSubElement(self, dom, index):
-        """Helper method to create new element."""
-        if dom.nodeType == dom.ELEMENT_NODE:
-            element = Directive(dom, config=self.config)
-            element.schema = self.config.getSchema(
-                dom.namespaceURI, dom.localName, self.schema)
-        elif dom.nodeType == dom.COMMENT_NODE:
-            element = Comment(dom)
-        else:
-            element = Element(dom)
-        locate(element, self, unicode(index))
-        return element
-
-    def getFullTagName(self):
-        """See configeditor.interfaces.IDirective"""
-        return self.domElement.tagName
         
-    def getAttribute(self, name):
-        """See configeditor.interfaces.IDirective"""
-        if name not in self.schema  and name+'_' not in self.schema:
-            raise AttributeError, "'%s' not in schema" %name
-        return self.domElement.getAttribute(name)
+    def __init__(self, name, schema, attrs, context, info, prefixes):
+        self.name = name
+        self.schema = schema
+        self.attrs = attrs
+        self.context = context
+        self.info = info
+        self.__parent__ = None
+        self.subs = []
+        self.prefixes = prefixes
 
-    def getAttributeMap(self):
-        """See configeditor.interfaces.IDirective"""
-        return dict(self.domElement.attributes.items())
-    
-    def getAvailableSubdirectives(self):
-        """ """
-        registry = self.config._registry
-        return [
-            ((ns, name), schema) 
-            for (ns, name), schema, usedIn, handler, info, parent in registry
-            if parent and self.schema.isOrExtends(parent.schema)]
-        
+    def __repr__(self):
+        return '<Directive %s>' %str(self.name)
 
-class Configuration(Directive):
-    """Cofiguration Object"""
-    implements(IConfiguration)
 
+class ZCMLFile(object):
+    """Representation of an entire ZCML file."""
+    implements(IZCMLFile)
+
     def __init__(self, filename, package, parent=None, name=None):
         # Retrieve the directive registry
         self.filename = filename
         self.package = package
         self.__parent__ = parent
         self.__name__ = name
-        self.config = self
-        self.schema = None
-                      
 
-    def _registry(self):
-        return getContext()._docRegistry
-    _registry = property(_registry)
-        
-    def domElement(self):
-        domElement = self.parse()
-        self.schema = self.getSchema(domElement.namespaceURI,
-                                     domElement.localName)
-        return domElement
-    domElement = Lazy(domElement)
+    def rootElement(self):
+        # Get the context that was originally generated during startup.
+        context = zope.app.appsetup.appsetup.getConfigContext()
+        context.package = self.package
 
-    def parse(self):
-        """See configeditor.interfaces.IConfiguration"""
-        return minidom.parse(self.filename).getElementsByTagName('configure')[0]
+        # Since we want to use a custom configuration handler, we need to
+        # instantiate the parser object ourselves 
+        parser = make_parser()
+        handler = MyConfigHandler(context)
+        parser.setContentHandler(handler)
+        parser.setFeature(feature_namespaces, True)
 
-    def getSchema(self, namespaceURI, tagName, parentSchema=None):
-        """See configeditor.interfaces.IConfiguration"""
-        if parentSchema is IZopeConfigure:
-            parentSchema = None
-        for (ns, name), schema, usedIn, handler, info, pDir in self._registry:
-            if ((ns == namespaceURI or ns == '') and name == tagName and
-                ((pDir is None and parentSchema is None) or
-                 (pDir is not None and parentSchema is pDir.schema))):
-                return schema
-        return None
+        # Now open the file
+        file = open(self.filename)
+        src = InputSource(getattr(file, 'name', '<string>'))
+        src.setByteStream(file)
 
-    def getNamespacePrefix(self, namespaceURI):
-        """ """
-        for name, value in self.getAttributeMap().items():
-            if name.startswith('xmlns') and value == namespaceURI:
-                if name == 'xmlns':
-                    return ''
-                else:
-                    return name[6:]
-        return None
+        # and parse it
+        parser.parse(src)
 
-    def getNamespaceURI(self, prefix):
-        """ """
-        for name, value in self.getAttributeMap().items():
-            if name == 'xmlns' and prefix == '':
-                return value
-            if name.startswith('xmlns') and name.endswith(prefix):
-                return value
+        # Finally we retrieve the root element, have it provide a special root
+        # directive interface and give it a location, so that we can do local
+        # lookups.
+        root = handler.rootElement
+        directlyProvides(root, IRootDirective)
+        root.__parent__ = self
+        return root
 
-        return None
-
-    def getResolver(self):
-        """ """
-        if self.package is None:
-            return None
-        resolver = ConfigurationContext()
-        resolver.package = self.package
-        resolver.i18n_domain = self.domElement.getAttribute('i18n_domain')
-        resolver.i18n_strings = {}
-        resolver.actions = []
-        from zope.configuration.xmlconfig import ParserInfo
-        resolver.info = ParserInfo(self.filename, 0, 0)
-        return resolver
+    rootElement = Lazy(rootElement)

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt	2005-02-26 14:13:49 UTC (rev 29308)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt	2005-02-26 14:16:04 UTC (rev 29309)
@@ -5,7 +5,7 @@
 <metal:block define-macro="zcml">
   <a href=""
       tal:attributes="href 
-         string:../../Code/${zcml/url}/index.html" 
+        string:../../Code/${zcml/url}/index.html?line=${zcml/line}#${zcml/line}"
       tal:content="zcml/file">
     zope/app/configure.zcml
   </a>



More information about the Zope3-Checkins mailing list