[Zope3-checkins] SVN: Zope3/trunk/ Milestone checkin of my apidoc tool refactorings. More to come. See

Stephan Richter srichter at cosmos.phy.tufts.edu
Wed Feb 23 17:22:49 EST 2005


Log message for revision 29269:
  Milestone checkin of my apidoc tool refactorings. More to come. See 
  CHANGES.txt for a list of changes.
  
  

Changed:
  U   Zope3/trunk/doc/CHANGES.txt
  U   Zope3/trunk/package-includes/apidoc-meta.zcml
  U   Zope3/trunk/src/zope/app/apidoc/README.txt
  D   Zope3/trunk/src/zope/app/apidoc/TODO.txt
  U   Zope3/trunk/src/zope/app/apidoc/__init__.py
  A   Zope3/trunk/src/zope/app/apidoc/apidoc.py
  U   Zope3/trunk/src/zope/app/apidoc/browser/apidoc.css
  U   Zope3/trunk/src/zope/app/apidoc/browser/apidoc.py
  U   Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml
  D   Zope3/trunk/src/zope/app/apidoc/classmodule/
  A   Zope3/trunk/src/zope/app/apidoc/classregistry.py
  A   Zope3/trunk/src/zope/app/apidoc/classregistry.txt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/__init__.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayClosingElement.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayComment.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function_index.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/menu.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/menu.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/browser.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/class_index.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/function.py
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/function_index.pt
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/menu.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/module.py
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/module_index.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/text.py
  A   Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py
  D   Zope3/trunk/src/zope/app/apidoc/codemodule/zcmlfile_index.pt
  A   Zope3/trunk/src/zope/app/apidoc/component.py
  A   Zope3/trunk/src/zope/app/apidoc/component.txt
  U   Zope3/trunk/src/zope/app/apidoc/configure.zcml
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/__init__.py
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.txt
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/ftests.py
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/iface_macros.pt
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/ifacemodule.py
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/index.pt
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/macros.py
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py
  A   Zope3/trunk/src/zope/app/apidoc/ifacemodule/presentation_macros.pt
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/tests.py
  A   Zope3/trunk/src/zope/app/apidoc/interface.py
  A   Zope3/trunk/src/zope/app/apidoc/interface.txt
  A   Zope3/trunk/src/zope/app/apidoc/presentation.py
  A   Zope3/trunk/src/zope/app/apidoc/presentation.txt
  U   Zope3/trunk/src/zope/app/apidoc/tests.py
  U   Zope3/trunk/src/zope/app/apidoc/utilities.py
  A   Zope3/trunk/src/zope/app/apidoc/utilities.txt
  A   Zope3/trunk/src/zope/app/apidoc/utilitymodule/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/__init__.py
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py
  A   Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.txt
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/ftests.py
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/tests.py
  A   Zope3/trunk/src/zope/app/apidoc/utilitymodule/utilitymodule.py
  U   Zope3/trunk/src/zope/app/apidoc/version.txt
  D   Zope3/trunk/src/zope/app/apidoc/viewmodule/
  A   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/__init__.py
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py
  A   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.txt
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/tests.py
  U   Zope3/trunk/src/zope/app/introspector/introspector.pt

-=-
Modified: Zope3/trunk/doc/CHANGES.txt
===================================================================
--- Zope3/trunk/doc/CHANGES.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/doc/CHANGES.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -10,23 +10,6 @@
 
     New features
 
-      - When raising the Unauthorized exception, the security checker
-        now passes the object in question in addition to the attribute
-        name and missing permission.  This should make debugging easier.
-
-      - You can declare "features" provided by your package with the new
-        <meta:provides feature="name" /> ZCML directive.  The presence of these
-        features can be tested with the new zcml:condition attribute.
-
-      - ZCML supports conditional directives using the zcml:condition
-        attribute.  The value of the attribute is an expression of the
-        form "verb arguments".  Currently the only recognized verb is "have",
-        and it takes a single name of a feature as an argument.
-
-        If the expression is true, the element the attribute is attached to
-        will be used, otherwise the element and its descendents will be
-        ignored.
-
       - API doctool has received some upgrades:
 
         * A new `bookmodule` compiles all our README.txt and other text
@@ -36,6 +19,11 @@
           `IContentType`) and then let's you see all interfaces providing this
           type (for example `IFile`)
 
+        * Views are now shown in the Interface details screen.
+
+        * Adapters are split into three categories: specific, extended, and
+          generic.
+
       - Zope 3 can now listen on specified network interfaces only.  Example
         of a server section in zope.conf:
 
@@ -265,6 +253,22 @@
 
     Restructuring
 
+      - Several changes have been made to the API doc tool:
+
+        + General inspection utilities are now *publically* available in the
+          modules: utilities, interface, component, presentation,
+          classregistry
+
+        + The interface details view has been broken up into macros, so that
+          the HTML code snippets can be reused.
+
+        + Converted most tests to text files.
+
+        + Moved code from `__init__.py` files to modules. 
+
+        + Removed `viewmodule`, since its functionality has been merged into
+          the interface details view.
+
       - Changed the test-counting mechanism for doctests.  Now a
         doctest count a test for every example that is preceeded by
         prose. (Although a doctest will never count less than 1 test.)
@@ -420,10 +424,6 @@
         Instead of `rename(container, oldName, newName)`, one should use
         `IContainerItemRenamer(container).renameItem(oldName, newName)`.
 
-      - Deprecated _configureCopy in zope.app.copypastemove.ObjectCopier. To
-        configure a recently copied object, suscribe to IObjectCopiedEvent for
-        the type of object you're interested in configuring.
-
     Bug Fixes
 
       - When I converted layers and skins to interfaces, I got the layer part
@@ -489,8 +489,6 @@
       - If the content-type was text/*, HTTPResponse.write re-set the
         content-length header to the first chunk of data.
 
-      - Object copy events weren't dispatched for object sublocations.
-
     Much thanks to everyone who contributed to this release:
 
       Jim Fulton, Fred Drake, Philipp von Weitershausen, Stephan Richter,

Modified: Zope3/trunk/package-includes/apidoc-meta.zcml
===================================================================
--- Zope3/trunk/package-includes/apidoc-meta.zcml	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/package-includes/apidoc-meta.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,5 +1,5 @@
 <configure xmlns:meta="http://namespaces.zope.org/meta">
-  <include package="zope.app.apidoc.classmodule" file="meta.zcml"/>
+  <include package="zope.app.apidoc.codemodule" file="meta.zcml"/>
   <include package="zope.app.apidoc.bookmodule" file="meta.zcml"/>
   <meta:provides feature="apidoc" />
 </configure>

Modified: Zope3/trunk/src/zope/app/apidoc/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/README.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/README.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,11 +1,61 @@
+========================
 Zope 3 API Documentation
 ========================
 
-This Zope 3 product provides a fully dynamic API documentation of Zope 3 and
-registered add-on components. The product is very extensible and can be easily
+This Zope 3 package provides fully dynamic API documentation of Zope 3 and
+registered add-on components. The package is very extensible and can be easily
 extended by implementing new modules.
 
+Besides being an application, the API doctool also provides several public
+APIs to extract information from various objects used by Zope 3.
 
+ * utilities -- Miscellaneous classes and functions that aid all documentation
+   modules. They are broadly usable.
+
+ * interface -- This module contains functions to inspect interfaces and
+   schemas.
+
+ * component -- This modules provides utility functions to lookup components
+   given an interface.
+
+ * presentation -- Presentation components are generally more complex than
+   others, so a separate utilities module is provided to inspect views.
+
+ * classregistry -- Here a simple dictionary-based registry for all known
+   classes is provided. It allows us to search in classes.
+
+
+Using the API Dcoumentation
+---------------------------
+
+The `APIDocumentation` class provides access to all available documentation
+modules. Documentation modules are utilities providing `IDocumentationModule`:
+
+
+  >>> from zope.app.testing import ztapi
+  >>> from zope.app.apidoc.interfaces import IDocumentationModule
+  >>> from zope.app.apidoc.ifacemodule.ifacemodule import InterfaceModule
+  >>> from zope.app.apidoc.zcmlmodule import ZCMLModule
+
+  >>> ztapi.provideUtility(IDocumentationModule, InterfaceModule(),
+  ...                      'Interface')
+  >>> ztapi.provideUtility(IDocumentationModule, ZCMLModule(), 'ZCML')
+
+Now we can instantiate the class (which is usually done when traversing
+'++apidoc++') and get a list of available modules:
+
+  >>> from zope.app.apidoc.apidoc import APIDocumentation
+  >>> doc = APIDocumentation(None, '++apidoc++')
+
+  >>> modules =  doc.keys()
+  >>> modules.sort()
+  >>> modules
+  [u'Interface', u'ZCML']
+
+  >>> doc['ZCML'] #doctest:+ELLIPSIS
+  <zope.app.apidoc.zcmlmodule.ZCMLModule object at ...>
+
+
 Developing a Module
 -------------------
 

Deleted: Zope3/trunk/src/zope/app/apidoc/TODO.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/TODO.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/TODO.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,6 +0,0 @@
-TO DO
-=====
-  
-- better README.txt
-
-- Refactor some of the views, so that templates can be reused

Modified: Zope3/trunk/src/zope/app/apidoc/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/__init__.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/__init__.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,84 +1,2 @@
-##############################################################################
-#
-# 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.
-#
-##############################################################################
-"""Zope 3 API Documentation
+# Make a package
 
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-from zope.interface import implements
-
-from zope.app import zapi
-from zope.app.location import locate
-from zope.app.location.interfaces import ILocation
-
-from interfaces import IDocumentationModule
-from utilities import ReadContainerBase
-
-class APIDocumentation(ReadContainerBase):
-    r"""Represent the complete API Documentation.
-
-    This documentation is implemented using a simply `IReadContainer`. The
-    items of the container are all registered utilities for
-    `IDocumentationModule`.
-
-    Demonstration:
-
-      >>> doc = APIDocumentation(None, '++apidoc++')
-      >>> doc.get('ZCML').title
-      u'ZCML Reference'
-
-      >>> doc.get('Documentation') is None
-      True
-
-      >>> print '\n'.join([id for id, mod in doc.items()])
-      Interface
-      ZCML
-    """
-
-    implements(ILocation)
-
-    def __init__(self, parent, name):
-        self.__parent__ = parent
-        self.__name__ = name
-    
-    def get(self, key, default=None):
-        """See zope.app.container.interfaces.IReadContainer"""
-        utility = zapi.queryUtility(IDocumentationModule, key, default)
-        if utility != default:
-            locate(utility, self, key)
-        return utility
-
-    def items(self):
-        """See zope.app.container.interfaces.IReadContainer"""
-        items = list(zapi.getUtilitiesFor(IDocumentationModule))
-        items.sort()
-        utils = []
-        for key, value in items:
-            locate(value, self, key)
-            utils.append((key, value))
-        return utils
-        
-class apidocNamespace(object):
-    """Used to traverse to an API Documentation."""
-    def __init__(self, ob, request=None):
-        self.context = ob
-        
-    def traverse(self, name, ignore):
-        return handleNamespace(self.context, name)
-
-def handleNamespace(ob, name):
-    """Used to traverse to an API Documentation."""
-    return APIDocumentation(ob, '++apidoc++'+name)
-    

Added: Zope3/trunk/src/zope/app/apidoc/apidoc.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/apidoc.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/apidoc.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,71 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Zope 3 API Documentation
+
+$Id: __init__.py 26777 2004-07-27 08:12:32Z hdima $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.interface import implements
+
+from zope.app import zapi
+from zope.app.location import locate
+from zope.app.location.interfaces import ILocation
+
+from interfaces import IDocumentationModule
+from utilities import ReadContainerBase
+
+class APIDocumentation(ReadContainerBase):
+    """Represent the complete API Documentation.
+
+    This documentation is implemented using a simply `IReadContainer`. The
+    items of the container are all registered utilities for
+    `IDocumentationModule`.
+    """
+    implements(ILocation)
+
+    def __init__(self, parent, name):
+        self.__parent__ = parent
+        self.__name__ = name
+    
+    def get(self, key, default=None):
+        """See zope.app.container.interfaces.IReadContainer"""
+        utility = zapi.queryUtility(IDocumentationModule, key, default)
+        if utility != default:
+            locate(utility, self, key)
+        return utility
+
+    def items(self):
+        """See zope.app.container.interfaces.IReadContainer"""
+        items = list(zapi.getUtilitiesFor(IDocumentationModule))
+        items.sort()
+        utils = []
+        for key, value in items:
+            locate(value, self, key)
+            utils.append((key, value))
+        return utils
+        
+
+class apidocNamespace(object):
+    """Used to traverse to an API Documentation."""
+    def __init__(self, ob, request=None):
+        self.context = ob
+        
+    def traverse(self, name, ignore):
+        return handleNamespace(self.context, name)
+
+def handleNamespace(ob, name):
+    """Used to traverse to an API Documentation."""
+    return APIDocumentation(ob, '++apidoc++'+name)
+    


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

Modified: Zope3/trunk/src/zope/app/apidoc/browser/apidoc.css
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/apidoc.css	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/browser/apidoc.css	2005-02-23 22:22:48 UTC (rev 29269)
@@ -73,6 +73,11 @@
     padding: 2px 0.5em 2px 0.5em;
 }
 
+h4 {
+  font-size: 90%;
+  font-style: italic;
+}
+
 p.breadcrumbs {
   padding: 2pt;
   border: solid 1px black;

Modified: Zope3/trunk/src/zope/app/apidoc/browser/apidoc.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/apidoc.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/browser/apidoc.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -25,21 +25,7 @@
     """View for the API Documentation"""
 
     def getModuleList(self):
-        """Get a list of all available documentation modules.
-
-        Example::
-
-          >>> from zope.app.apidoc import APIDocumentation
-          >>> from zope.publisher.browser import TestRequest
-          
-          >>> view = APIDocumentationView()
-          >>> view.request = TestRequest()
-          >>> view.context = APIDocumentation(None, '++apidoc++')
-          >>> info = view.getModuleList()
-          >>> info = [(i['name'], i['title']) for i in info]
-          >>> print info
-          [(u'Interface', u'Interfaces'), (u'ZCML', u'ZCML Reference')]
-        """
+        """Get a list of all available documentation modules."""
         items = list(self.context.items())
         items.sort()
         result = []

Modified: Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -27,7 +27,7 @@
       />
 
   <pages
-    for="zope.app.apidoc.APIDocumentation"
+    for="zope.app.apidoc.apidoc.APIDocumentation"
     class=".apidoc.APIDocumentationView"
     permission="zope.app.apidoc.UseAPIDoc">
 

Added: Zope3/trunk/src/zope/app/apidoc/classregistry.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classregistry.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/classregistry.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,60 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Class Registry
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+import sys
+
+from zope.app import zapi
+
+class ClassRegistry(dict):
+    """A simple registry for classes."""
+
+    def getClassesThatImplement(self, iface):
+        """Return all class items that implement iface.
+
+        Methods returns a list of 2-tuples of the form (path, class).
+        """
+        return [(path, klass) for path, klass in self.items()
+                if iface.implementedBy(klass)]
+
+    def getSubclassesOf(self, klass):
+        """Return all class items that are proper subclasses of klass.
+
+        Methods returns a list of 2-tuples of the form (path, class).
+        """
+        return [(path, klass2) for path, klass2 in self.items()
+                if issubclass(klass2, klass) and klass2 is not klass]
+
+
+classRegistry = ClassRegistry()
+
+def cleanUp():
+    classRegistry.clear()
+
+from zope.testing.cleanup import addCleanUp
+addCleanUp(cleanUp)
+
+
+def safe_import(path, default=None):
+    """Import a given path as efficiently as possible and without failure."""
+    module = sys.modules.get(path, default)
+    if module is default:
+        try:
+            module = __import__(path, {}, {}, ('*',))
+        except ImportError:
+            return default
+    return module

Added: Zope3/trunk/src/zope/app/apidoc/classregistry.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classregistry.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/classregistry.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,146 @@
+==================
+The Class Registry
+==================
+
+This little registry allows us to quickly query a complete list of classes
+that are defined and used by Zope 3. The prime feature of the class is the
+'getClassesThatImplement(iface)' method that returns all classes that
+implement the passed interface. Another method, 'getSubclassesOf(klass)'
+returns all registered subclassess of the given class.
+
+The class registry, subclassing the dictionary type, can be instantiated like
+any other dictionary:
+
+  >>> from zope.app.apidoc.classregistry import ClassRegistry
+  >>> reg = ClassRegistry()
+
+Let's now add a couple of classes to registry. The classes should implement
+some interfaces, so that we can test all methods on the class registry:
+
+  >>> from zope.interface import Interface, implements
+
+  >>> class IA(Interface):
+  ...      pass
+  >>> class IB(IA):
+  ...      pass
+  >>> class IC(Interface):
+  ...      pass
+  >>> class ID(Interface):
+  ...      pass
+
+  >>> class A(object):
+  ...    implements(IA)
+  >>> reg['A'] = A
+
+  >>> class B:
+  ...    implements(IB)
+  >>> reg['B'] = B
+
+  >>> class B2(object):
+  ...    implements(IB)
+  >>> reg['B2'] = B2
+
+  >>> class C(object):
+  ...    implements(IC)
+  >>> reg['C'] = C
+  >>> class A2(A):
+  ...    pass
+  >>> reg['A2'] = A2
+
+Since the registry is just a dictionary, we can ask for all its keys, which
+are the names of the classes:
+
+  >>> names = reg.keys()
+  >>> names.sort()
+  >>> names
+  ['A', 'A2', 'B', 'B2', 'C']
+
+  >>> reg['A'] is A
+  True
+
+There are two API methods specific to the class registry:
+
+`getClassesThatImplement(iface)`
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method returns all classes that implement the specified interface:
+
+  >>> pprint(reg.getClassesThatImplement(IA)) #doctest:+ELLIPSIS
+  [('A', <class 'A'>),
+   ('B', <class __builtin__.B at ...>),
+   ('A2', <class 'A2'>),
+   ('B2', <class 'B2'>)]
+
+  >>> pprint(reg.getClassesThatImplement(IB)) #doctest:+ELLIPSIS
+  [('B', <class __builtin__.B at ...>),
+   ('B2', <class 'B2'>)]
+
+  >>> pprint(reg.getClassesThatImplement(IC))
+  [('C', <class 'C'>)]
+
+  >>> pprint(reg.getClassesThatImplement(ID))
+  []
+
+`getSubclassesOf(klass)`
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+This method will find all classes that inherit the specified class: 
+
+  >>> pprint(reg.getSubclassesOf(A))
+  [('A2', <class 'A2'>)]
+
+  >>> pprint(reg.getSubclassesOf(B))
+  []
+
+
+Safe Imports
+------------
+
+Using the `safe_import()` we can quickly look up modules by minimizing import
+calls.
+
+  >>> from zope.app.apidoc.classregistry import safe_import
+
+First we try to find the path in 'sys.modules', since this lookup is much
+more efficient than importing it. If it was not found, we go back and try
+to import the path. If that also fails, we return the 'default' value.
+
+Here are some examples::
+
+  >>> import sys
+  >>> 'zope.app' in sys.modules
+  True
+
+  >>> safe_import('zope.app') is sys.modules['zope.app']
+  True
+
+  >>> safe_import('weirdname') is None
+  True
+
+For this example, we'll create a dummy module:
+
+  >>> import os
+  >>> here = os.path.dirname(__file__)
+  >>> filename = os.path.join(here, 'testmodule.py')
+  >>> f = open(filename, 'w')
+  >>> f.write('# dummy module\n')
+  >>> f.close()
+
+The temporary module is not already imported, but will be once
+we've called safe_import():
+
+  >>> module_name = 'zope.app.apidoc.testmodule'
+  >>> module_name in sys.modules
+  False
+  >>> safe_import(module_name).__name__ == module_name
+  True
+  >>> module_name in sys.modules
+  True
+
+Now clean up the temporary module, just to play nice:
+
+  >>> os.unlink(filename)
+  >>> if os.path.exists(filename + 'c'):
+  ...     os.unlink(filename + 'c')
+  >>> if os.path.exists(filename + 'o'):
+  ...     os.unlink(filename + 'o')

Copied: Zope3/trunk/src/zope/app/apidoc/codemodule (from rev 29222, Zope3/trunk/src/zope/app/apidoc/classmodule)

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/__init__.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/__init__.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Class Documentation Module
+"""Code Documentation Module
 
 This module is able to take a dotted name of any class and display
 documentation for it.
@@ -19,34 +19,15 @@
 $Id$
 """
 __docformat__ = 'restructuredtext'
-
-import os
-import sys
-import inspect
-from types import ClassType, TypeType, FunctionType
-
-import zope
-import zope.deprecation
-from zope.security.checker import getCheckerForInstancesOf
-from zope.interface import Interface, Attribute, implements, implementedBy
-
+from zope.interface import Interface, implements
 from zope.app import zapi
-from zope.app.container.interfaces import IReadContainer
 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.utilities import ReadContainerBase
-from zope.app.apidoc.utilities import getPythonPath
-from zope.app.apidoc.utilities import getPublicAttributes
-from zope.app.apidoc.utilities import getInterfaceForAttribute
-from zope.app.apidoc.utilities import getFunctionSignature
+from zope.app.apidoc.classregistry import safe_import
+from zope.app.apidoc.codemodule.module import Module
 
-# 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 IAPIDocRootModule(Interface):
     """Marker interface for utilities that represent class browser root
@@ -56,510 +37,14 @@
     dotted name.
     """ 
 
-class IModuleDocumentation(IReadContainer):
-    """Representation of a Python module for documentation.
 
-    The items of the container are sub-modules and classes.
-    """
-    def getDocString():
-        """Return the doc string of the module."""
+class CodeModule(Module):
+    """Represent the Documentation of any possible source code in the packages.
 
-    def getFileName():
-        """Return the file name of the module."""
-
-    def getPath():
-        """Return the Python path of the module."""
-
-
-class IClassDocumentation(Interface):
-    """Representation of a class or type for documentation."""
-
-    def getDocString():
-        """Return the doc string of the class."""
-
-    def getPath():
-        """Return the Python path of the class."""
-
-    def getBases():
-        """Return the base classes of the class."""
-
-    def getKnownSubclasses():
-        """Return the known subclasses classes of the class."""
-
-    def getInterfaces():
-        """Return the interfaces the class implements."""
-
-    def getAttributes():
-        """Return a list of 3-tuple attribute information.
-
-        The first entry of the 3-tuple is the name of the attribute, the
-        second is the attribute object itself. The third entry is the
-        interface in which the attribute is defined.
-
-        Note that only public attributes are returned, meaning only attributes
-        that do not start with an '_'-character.
-        """
-
-    def getMethods():
-        """Return a list of 3-tuple method information.
-
-        The first entry of the 3-tuple is the name of the method, the
-        second is the method object itself. The third entry is the
-        interface in which the method is defined.
-
-        Note that only public methods are returned, meaning only methods
-        that do not start with an '_'-character.
-        """
-
-    def getSecurityChecker():
-        """Return the security checker for this class.
-
-        Since 99% of the time we are dealing with name-based security
-        checkers, we can look up the get/set permission required for a
-        particular class attribute/method.
-        """
-
-class IFunctionDocumentation(Interface):
-    """Representation of a function for documentation."""
-
-    def getDocString():
-        """Return the doc string of the function."""
-
-    def getPath():
-        """Return the Python path of the function."""
-
-    def getSignature():
-        """Return the signature of the function as a string."""
-
-    def getAttributes():
-        """Return a list of 2-tuple attribute information.
-
-        The first entry of the 2-tuple is the name of the attribute, the
-        second is the attribute object itself.
-        """
-
-class IZCMLFileDocumentation(Interface):
-    """Representation of a function for documentation."""
-
-    path = Attribute('Full path of the ZCML file.')
-
-
-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']
-    """
-    implements(ILocation, IModuleDocumentation)
-
-    def __init__(self, parent, name, module, setup=True):
-        """Initialize object."""
-        self.__parent__ = parent
-        self.__name__ = name
-        self.__module = module
-        self.__children = {}
-        if setup:
-            self.__setup()
-
-    def __setup(self):
-        """Setup the module sub-tree."""
-        # Detect packages
-        if hasattr(self.__module, '__file__') and \
-               (self.__module.__file__.endswith('__init__.py') or
-                self.__module.__file__.endswith('__init__.pyc')or
-                self.__module.__file__.endswith('__init__.pyo')):
-            dir = os.path.dirname(self.__module.__file__)
-            for file in os.listdir(dir):
-                if file in IGNORE_FILES:
-                    continue
-                path = os.path.join(dir, file)
-
-                if os.path.isdir(path) and '__init__.py' in os.listdir(path):
-                    fullname = self.__module.__name__ + '.' + file
-                    module = safe_import(fullname)
-                    if module is not None:
-                        self.__children[file] = Module(self, file, module)
-
-                elif os.path.isfile(path) and file.endswith('.py') and \
-                         not file.startswith('__init__'):
-                    name = file[:-3]
-                    fullname = self.__module.__name__ + '.' + name
-                    module = safe_import(fullname)
-                    if module is not None:
-                        self.__children[name] = Module(self, name, module)
-
-                elif os.path.isfile(path) and file.endswith('.zcml'):
-                    self.__children[file] = ZCMLFile(self, file, path)
-
-        # Setup classes in module, if any are available.
-        zope.deprecation.__show__.off()
-        for name in self.__module.__dict__.keys():
-            attr = getattr(self.__module, name)
-            # We do not want to register duplicates or non-"classes"
-            if hasattr(attr, '__module__') and \
-                   attr.__module__ == self.__module.__name__:
-
-                if type(attr) in (ClassType, TypeType) and \
-                       attr.__name__ == name:
-                    self.__children[attr.__name__] = Class(self, name, attr)
-
-                elif type(attr) is FunctionType and not name.startswith('_'):
-                    self.__children[attr.__name__] = Function(self, name, attr)
-        zope.deprecation.__show__.on()
-
-
-    def getDocString(self):
-        """See IModule."""
-        return self.__module.__doc__
-
-    def getFileName(self):
-        """See IModule."""
-        return self.__module.__file__
-
-    def getPath(self):
-        """See IModule."""
-        return self.__module.__name__
-
-    def get(self, key, default=None):
-        """See zope.app.container.interfaces.IReadContainer."""
-        obj = self.__children.get(key, default)
-        if obj is not default:
-            return obj
-
-        # We are actually able to find much more than we promise
-        if self.getPath():
-            path = self.getPath() + '.' + key
-        else:
-            path = key
-        obj = safe_import(path)
-        if obj is not None:
-            return Module(self, key, obj)
-
-        return default
-
-    def items(self):
-        """See zope.app.container.interfaces.IReadContainer."""
-        return self.__children.items()
-
-
-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)
-
-    """
-    implements(ILocation, IClassDocumentation)
-
-    def __init__(self, module, name, klass):
-        self.__parent__ = module
-        self.__name__ = name
-        self.__klass = klass
-
-        # Setup interfaces that are implemented by this class.
-        self.__interfaces = list(implementedBy(klass))
-        all_ifaces = {}
-        for iface in self.__interfaces:
-            all_ifaces[getPythonPath(iface)] = iface
-            for base in [base for base in iface.__bases__]:
-                all_ifaces[getPythonPath(base)] = base
-        self.__all_ifaces = all_ifaces.values()
-
-        # Register the class with the global class registry.
-        global classRegistry
-        classRegistry[self.getPath()] = klass
-
-    def getPath(self):
-        """See IClassDocumentation."""
-        return self.__parent__.getPath() + '.' + self.__name__
-
-    def getDocString(self):
-        """See IClassDocumentation."""
-        return self.__klass.__doc__
-
-    def getBases(self):
-        """See IClassDocumentation."""
-        return self.__klass.__bases__
-
-    def getKnownSubclasses(self):
-        """See IClassDocumentation."""
-        global classRegistry
-        return [k for n, k in classRegistry.getSubclassesOf(self.__klass)]
-
-    def getInterfaces(self):
-        """See IClassDocumentation."""
-        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>)]
-        """
-        return [
-            (name, getattr(self.__klass, name),
-             getInterfaceForAttribute(name, self.__all_ifaces, asPath=False))
-
-            for name in getPublicAttributes(self.__klass)
-            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>)]
-        """
-        return [
-            (name, getattr(self.__klass, name),
-             getInterfaceForAttribute(name, self.__all_ifaces, asPath=False))
-
-            for name in getPublicAttributes(self.__klass)
-            if inspect.ismethod(getattr(self.__klass, name))]
-
-    def getSecurityChecker(self):
-        """See IClassDocumentation."""
-        return getCheckerForInstancesOf(self.__klass)
-
-
-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()
-      []
-    """
-    implements(ILocation, IFunctionDocumentation)
-
-    def __init__(self, module, name, func):
-        self.__parent__ = module
-        self.__name__ = name
-        self.__func = func
-
-    def getPath(self):
-        """See IFunctionDocumentation."""
-        return self.__parent__.getPath() + '.' + self.__name__
-
-    def getDocString(self):
-        """See IFunctionDocumentation."""
-        return self.__func.__doc__
-
-    def getSignature(self):
-        """See IFunctionDocumentation."""
-        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')]
-        """
-        return self.__func.__dict__.items()
-
-
-class ZCMLFile(object):
-    """Represent the documentation of any ZCML file.
-
-    This object in itself is rather simple, since it only stores the full path
-    of the ZCML file and its location in the documentation tree.
-
-    >>> zcml = ZCMLFile(None, 'foo.zcml', '/Zope3/src/zope/app/foo.zcml')
-    >>> zcml.__parent__ is None
-    True
-    >>> zcml.__name__
-    'foo.zcml'
-    >>> zcml.path
-    '/Zope3/src/zope/app/foo.zcml'
-    """
-    
-    implements(ILocation, IZCMLFileDocumentation)
-
-    def __init__(self, module, name, path):
-        """Initialize the object."""
-        self.__parent__ = module
-        self.__name__ = name
-        self.path = path
-
-
-class ClassModule(Module):
-    """Represent the Documentation of any possible class.
-
     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 = ClassModule()
+      >>> cm = CodeModule()
 
       >>> cm.getDocString()
       u'Zope 3 root.'
@@ -570,13 +55,13 @@
 
       >>> names = cm.keys()
       >>> names.sort()
-      >>> names == [u'zope']
-      True
+      >>> names
+      [u'zope']
     """
     implements(IDocumentationModule)
 
     # See zope.app.apidoc.interfaces.IDocumentationModule
-    title = _('Classes')
+    title = _('Code Browser')
 
     # See zope.app.apidoc.interfaces.IDocumentationModule
     description = _("""
@@ -602,13 +87,16 @@
     """)
     def __init__(self):
         """Initialize object."""
-        super(ClassModule, self).__init__(None, '', None, False)
+        super(CodeModule, self).__init__(None, '', None, False)
         self.__isSetup = False
 
-    def __setup(self):
+    def setup(self):
         """Setup module and class tree."""
+        if self.__isSetup:
+            return
         for name, mod in zapi.getUtilitiesFor(IAPIDocRootModule):
-            self._Module__children[name] = Module(self, name, safe_import(mod))
+            self._children[name] = Module(self, name, safe_import(mod))
+        self.__isSetup = True
 
     def getDocString(self):
         """See Module class."""
@@ -624,161 +112,10 @@
 
     def get(self, key, default=None):
         """See zope.app.container.interfaces.IReadContainer."""
-        if self.__isSetup is False:
-            self.__setup()
-            self.__isSetup = True
-        return super(ClassModule, self).get(key, default)
+        self.setup()
+        return super(CodeModule, self).get(key, default)
 
     def items(self):
         """See zope.app.container.interfaces.IReadContainer."""
-        if self.__isSetup is False:
-            self.__setup()
-            self.__isSetup = True
-        return super(ClassModule, self).items()
-
-
-class ClassRegistry(dict):
-    """A simple registry for classes.
-
-    This little registry allows us to quickly query a complete list of classes
-    that are defined and used by Zope 3. The prime feature of the class is the
-    'getClassesThatImplement(iface)' method that returns all classes that
-    implement the passed interface. Another method, 'getSubclassesOf(klass)'
-    returns all registered subclassess of the given class.
-
-    Here is the registry in action::
-
-      >>> reg = ClassRegistry()
-
-      >>> class IA(Interface):
-      ...      pass
-      >>> class IB(IA):
-      ...      pass
-      >>> class IC(Interface):
-      ...      pass
-      >>> class ID(Interface):
-      ...      pass
-
-      >>> class A(object):
-      ...    implements(IA)
-      >>> reg['A'] = A
-      >>> class B:
-      ...    implements(IB)
-      >>> reg['B'] = B
-      >>> class B2(object):
-      ...    implements(IB)
-      >>> reg['B2'] = B2
-      >>> class C(object):
-      ...    implements(IC)
-      >>> reg['C'] = C
-      >>> class A2(A):
-      ...    pass
-      >>> reg['A2'] = A2
-
-      >>> names = reg.keys()
-      >>> names.sort()
-      >>> names
-      ['A', 'A2', 'B', 'B2', 'C']
-
-      >>> reg['A'] is A
-      True
-
-      >>> def names(l):
-      ...   l = [n for n, k in l]
-      ...   l.sort()
-      ...   return l
-
-      >>> names(reg.getClassesThatImplement(IA))
-      ['A', 'A2', 'B', 'B2']
-      >>> names(reg.getClassesThatImplement(IB))
-      ['B', 'B2']
-      >>> names(reg.getClassesThatImplement(IC))
-      ['C']
-      >>> names(reg.getClassesThatImplement(ID))
-      []
-
-      >>> names(reg.getSubclassesOf(A))
-      ['A2']
-      >>> names(reg.getSubclassesOf(B))
-      []
-
-    """
-
-    def getClassesThatImplement(self, iface):
-        """Return all class items that implement iface.
-
-        Methods returns a list of 2-tuples of the form (path, class).
-        """
-        return [(path, klass) for path, klass in self.items()
-                if iface.implementedBy(klass)]
-
-    def getSubclassesOf(self, klass):
-        """Return all class items that are proper subclasses of klass.
-
-        Methods returns a list of 2-tuples of the form (path, class).
-        """
-        return [(path, klass2) for path, klass2 in self.items()
-                if issubclass(klass2, klass) and klass2 is not klass]
-
-
-classRegistry = ClassRegistry()
-
-def cleanUp():
-    classRegistry.clear()
-
-from zope.testing.cleanup import addCleanUp
-addCleanUp(cleanUp)
-
-
-def safe_import(path, default=None):
-    r"""Import a given path as efficiently as possible and without failure.
-
-    First we try to find the path in 'sys.modules', since this lookup is much
-    more efficient than importing it. If it was not found, we go back and try
-    to import the path. If that also fails, we return the 'default' value.
-
-    Here are some examples::
-
-      >>> 'zope.app' in sys.modules
-      True
-      >>> safe_import('zope.app') is sys.modules['zope.app']
-      True
-
-      >>> safe_import('weirdname') is None
-      True
-
-    For this example, we'll create a dummy module:
-
-      >>> here = os.path.dirname(__file__)
-      >>> filename = os.path.join(here, 'testmodule.py')
-      >>> f = open(filename, 'w')
-      >>> f.write('# dummy module\n')
-      >>> f.close()
-
-    The temporary module is not already imported, but will be once
-    we've called safe_import():
-
-      >>> module_name = __name__ + '.testmodule'
-      >>> module_name in sys.modules
-      False
-      >>> safe_import(module_name).__name__ == module_name
-      True
-      >>> module_name in sys.modules
-      True
-      >>> del sys.modules[module_name]
-
-    Now clean up the temporary module, just to play nice:
-
-      >>> os.unlink(filename)
-      >>> if os.path.exists(filename + 'c'):
-      ...     os.unlink(filename + 'c')
-      >>> if os.path.exists(filename + 'o'):
-      ...     os.unlink(filename + 'o')
-    """
-    module = sys.modules.get(path, default)
-    if module is default:
-        try:
-            module = __import__(path, {}, {}, ('*',))
-        except ImportError:
-            return default
-    return module
+        self.setup()
+        return super(CodeModule, self).items()

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/__init__.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/__init__.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1 @@
+# Make a package.


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/class_.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,255 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Class Views
+
+$Id: browser.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+import types
+from zope.proxy import removeAllProxies
+from zope.security.proxy import removeSecurityProxy
+
+from zope.app import zapi
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.utilities import getPythonPath, getPermissionIds
+from zope.app.apidoc.utilities import renderText, getFunctionSignature
+
+
+def getTypeLink(type):
+    if type is types.NoneType:
+        return None
+    path = getPythonPath(type)
+    return path.replace('.', '/')
+
+class ClassDetails(object):
+    """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'}]
+        """
+        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())
+          []
+        """
+        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'}]
+        """
+        info = []
+        codeModule = zapi.getUtility(IDocumentationModule, "Code")
+        for cls in classes:
+            # We need to removeAllProxies because the security checkers for
+            # zope.app.container.contained.ContainedProxy and
+            # zope.app.i18n.messagecatalog.MessageCatalog prevent us from
+            # accessing __name__ and __module__.
+            unwrapped_cls = removeAllProxies(cls)
+            path = getPythonPath(unwrapped_cls)
+            try:
+                klass = zapi.traverse(codeModule, path.replace('.', '/'))
+                url = zapi.absoluteURL(klass, self.request)
+            except TraversalError:
+                # If one of the classes is implemented in C, we will not
+                # be able to find it.
+                url = None
+            info.append({'path': path, 'url': url})
+        return info
+
+
+    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'
+        """
+        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']
+        """
+        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)]
+        """
+        attrs = []
+        for name, attr, iface in self.context.getAttributes():
+            entry = {'name': name,
+                     'value': `attr`,
+                     'type': type(attr).__name__,
+                     'type_link': getTypeLink(type(attr)),
+                     'interface': getPythonPath(iface)}
+            # Since checkers do not have security declarations on their API,
+            # we have to remove the security wrapper to get to the API calls. 
+            checker = self.context.getSecurityChecker()
+            entry.update(
+                getPermissionIds(name, removeSecurityProxy(checker)))
+            attrs.append(entry)
+        return attrs
+
+
+    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)]]
+        """
+        methods = []
+        # remove the security proxy, so that `attr` is not proxied. We could
+        # unproxy `attr` for each turn, but that would be less efficient.
+        #
+        # `getPermissionIds()` also expects the class's security checker not
+        # to be proxied.
+        klass = removeSecurityProxy(self.context)
+        for name, attr, iface in klass.getMethods():
+            entry = {'name': name,
+                     'signature': getFunctionSignature(attr),
+                     'doc': renderText(attr.__doc__ or '',
+                                       zapi.getParent(self.context).getPath()),
+                     'interface': getPythonPath(iface)}
+            entry.update(getPermissionIds(name, klass.getSecurityChecker()))
+            methods.append(entry)
+        return methods
+
+
+    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>
+        """
+        return renderText(self.context.getDocString() or '',
+                          zapi.getParent(self.context).getPath())
+


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/class_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,176 @@
+<html metal:use-macro="views/apidoc_macros/details">
+<body metal:fill-slot="contents">
+
+  <h1 class="details-header"
+      tal:content="context/getPath">
+    zope.app.Klass
+  </h1>
+
+  <div class="indent">
+    <div class="documentation" tal:content="structure view/getDoc">
+      Here is the doc string
+    </div>
+  </div>
+
+
+  <h2 class="details-section" i18n:translate="">Bases</h2>
+
+    <div class="indent"
+         tal:define="bases view/getBases">
+
+    <ul class="attr-list" tal:condition="bases">
+      <li tal:repeat="base bases">
+        <a href=""
+            tal:attributes="href string:${base/url}/index.html"
+            tal:content="base/path"
+            tal:condition="base/url" />
+        <div tal:condition="not: base/url">
+          <span tal:replace="base/path" />
+          <span i18n:translate="">(C-based class)</span>
+        </div>
+      </li>
+    </ul>
+
+    <p tal:condition="not: bases">
+      <em i18n:translate="">There are no base classes.</em>
+    </p>
+
+    </div>
+
+  <h2 class="details-section" i18n:translate="">Implemented Interfaces</h2>
+
+    <div class="indent"
+         tal:define="ifaces view/getInterfaces">
+
+    <ul class="attr-list" tal:condition="ifaces">
+      <li tal:repeat="iface ifaces">
+        <a href=""
+           tal:attributes="href
+               string:${view/getBaseURL}/Interface/$iface/apiindex.html"
+           tal:content="iface" />
+      </li>
+    </ul>
+
+    <p tal:condition="not: ifaces">
+      <em i18n:translate="">There are no implemented interfaces.</em>
+    </p>
+
+    </div>
+
+
+  <h2 class="details-section" i18n:translate="">Attributes/Properties</h2>
+
+  <div class="indent"
+      tal:define="attributes view/getAttributes">
+
+  <ul class="attr-list" tal:condition="attributes">
+
+    <li tal:repeat="attr attributes">
+      <b><code tal:content="attr/name">attr</code></b>
+      <tal:omit-tag condition="not: attr/type_link">
+        (type: <code tal:content="attr/type" />)
+      </tal:omit-tag>
+      <tal:omit-tag condition="attr/type_link">
+        (<span i18n:translate="">type:</span>
+        <a href=""
+           tal:attributes="href 
+               string:${view/getBaseURL}/Code/${attr/type_link}/index.html">
+          <code tal:content="attr/type" /></a>)
+      </tal:omit-tag>
+      <br/>
+      <i i18n:translate="">Value:</i>
+      <code tal:content="attr/value">u''</code><br />
+
+      <span class="small" tal:condition="attr/interface">
+        <i i18n:translate="">Interface:</i>
+        <a href=""
+           tal:attributes="href
+          string:${view/getBaseURL}/Interface/${attr/interface}/apiindex.html"
+           tal:content="attr/interface">Iface</a><br />
+      </span>
+      <span class="small"
+          tal:condition="python: attr['read_perm'] and attr['write_perm']">
+        <i i18n:translate="">Permissions:</i>
+        <span tal:replace="attr/read_perm">zope.View</span>
+            <span i18n:translate="">(read)</span>,
+        <span tal:replace="attr/write_perm">zope.View</span>
+            <span i18n:translate="">(write)</span>
+      </span>
+    </li>
+
+  </ul>
+
+  <p tal:condition="not: attributes">
+    <em i18n:translate="">There are no attributes in this class.</em>
+  </p>
+
+  </div>
+
+
+  <h2 class="details-section" i18n:translate="">Methods</h2>
+
+  <div class="indent"
+      tal:define="methods view/getMethods">
+
+  <ul class="attr-list" tal:condition="methods">
+
+    <li tal:repeat="method view/getMethods">
+      <b><code
+          tal:content="string:${method/name}${method/signature}" />
+      </b><br>
+      <div class="inline-documentation" tal:content="structure method/doc">
+        method desc
+      </div>
+
+      <span class="small" tal:condition="method/interface">
+        <i i18n:translate="">Interface:</i>
+        <a href=""
+           tal:attributes="href
+          string:${view/getBaseURL}/Interface/${method/interface}/apiindex.html"
+           tal:content="method/interface">Iface</a><br/>
+      </span>
+
+      <span class="small"
+          tal:condition="python: method['read_perm'] and method['write_perm']">
+        <i i18n:translate="">Permissions:</i>
+        <span tal:replace="method/read_perm">zope.View</span>
+            <span i18n:translate="">(read)</span>,
+        <span tal:replace="method/write_perm">zope.View</span>
+            <span i18n:translate="">(write)</span>
+      </span>
+    </li>
+
+  </ul>
+
+  <p tal:condition="not: methods">
+    <em i18n:translate="">There are no methods in this class.</em>
+  </p>
+
+  </div>
+
+  <h2 class="details-section" i18n:translate="">Known Subclasses</h2>
+
+  <div class="indent"
+       tal:define="subclasses view/getKnownSubclasses">
+
+    <ul class="attr-list" tal:condition="subclasses">
+      <li tal:repeat="cls subclasses">
+        <a href=""
+            tal:attributes="href string:${cls/url}/index.html"
+            tal:content="cls/path"
+            tal:condition="cls/url" />
+        <div tal:condition="not: cls/url">
+          <span tal:replace="cls/path" />
+          <span i18n:translate="">(C-based class)</span>
+        </div>
+      </li>
+    </ul>
+
+    <p tal:condition="not: subclasses">
+      <em i18n:translate="">There are no known subclasses.</em>
+    </p>
+
+  </div>
+
+</body>
+</html>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/configure.zcml	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,62 @@
+<configure
+    xmlns="http://namespaces.zope.org/browser"
+    xmlns:zope="http://namespaces.zope.org/zope">
+
+  <page
+      for="..CodeModule"
+      permission="zope.app.apidoc.UseAPIDoc"
+      class=".menu.Menu"
+      name="menu.html"
+      template="menu.pt" />
+
+  <page
+      for="..interfaces.IModuleDocumentation"
+      permission="zope.app.apidoc.UseAPIDoc"
+      class=".module.ModuleDetails"
+      name="index.html"
+      template="module_index.pt" />
+
+  <page
+      for="..interfaces.IClassDocumentation"
+      permission="zope.app.apidoc.UseAPIDoc"
+      class=".class_.ClassDetails"
+      name="index.html"
+      template="class_index.pt" />
+
+  <page
+      for="..interfaces.IFunctionDocumentation"
+      permission="zope.app.apidoc.UseAPIDoc"
+      class=".function.FunctionDetails"
+      name="index.html"
+      template="function_index.pt" />
+
+  <page
+      for="..text.TextFile"
+      permission="zope.app.apidoc.UseAPIDoc"
+      class=".text.TextFileDetails"
+      name="index.html"
+      template="textfile_index.pt" />
+
+  <!-- ZCML File -->
+  <page
+      for="..interfaces.IConfiguration"
+      name="index.html"
+      template="zcmlfile_index.pt"
+      permission="zope.View"/>
+
+  <page
+      name="display"
+      for="..interfaces.IDirective"
+      template="displayDirective.pt"
+      class=".zcml.DisplayDirective"
+      permission="zope.ManageContent"/>
+
+  <page
+      name="display"
+      for="..interfaces.IComment"
+      template="displayComment.pt"
+      class=".zcml.DisplayComment"
+      permission="zope.ManageContent"/>
+
+
+</configure>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayClosingElement.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/displayClosingElement.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayClosingElement.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,3 @@
+<div class="directive">
+  &lt;/<span class="tagName" tal:content="context/tagName">tagName</span>&gt;
+</div>  


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayComment.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/displayComment.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayComment.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,4 @@
+<br />
+<div class="commentElement">
+    &lt;!-- <tal:block replace="view/value" /> --&gt;
+</div>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/displayDirective.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/displayDirective.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,30 @@
+<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>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/function.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Function Views
+
+$Id: browser.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.app import zapi
+from zope.app.apidoc.utilities import getPythonPath, renderText
+
+class FunctionDetails(object):
+    """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'
+        """
+        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')]
+        """
+        return [{'name': name,
+                 'value': `attr`,
+                 'type': type(attr).__name__,
+                 'type_link': getPythonPath(type(attr)).replace('.', '/')}
+                
+                for name, attr in self.context.getAttributes()]


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/function_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/function_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,61 @@
+<html metal:use-macro="views/apidoc_macros/details">
+<body metal:fill-slot="contents">
+
+  <h1 class="details-header"
+      tal:content="context/getPath">
+    zope.app.function
+  </h1>
+
+  <h2 class="details-section" i18n:translate="">Signature</h2>
+
+  <div class="indent">
+    <b><code
+        tal:content="string:${context/zope:name}${context/getSignature}" />
+    </b>
+  </div>
+
+  <div tal:condition="context/getDocString">
+    <h2 class="details-section" i18n:translate="">Documentation String</h2>
+
+    <div class="indent">
+      <div class="documentation" tal:content="structure view/getDocString">
+        Here is the doc string
+      </div>
+    </div>
+  </div>
+
+  <div 
+      tal:define="attributes view/getAttributes"      
+      tal:condition="attributes">
+
+  <h2 class="details-section" i18n:translate="">Attributes</h2>
+
+  <div class="indent">
+
+    <ul class="attr-list">
+    
+      <li tal:repeat="attr attributes">
+        <b><code tal:content="attr/name">attr</code></b>
+        <tal:omit-tag condition="not: attr/type_link">
+          (<span i18n:translate="">type:</span>
+            <code tal:content="attr/type" />)
+        </tal:omit-tag>
+        <tal:omit-tag condition="attr/type_link">
+          (<span i18n:translate="">type:</span>
+          <a href="" 
+             tal:attributes="href 
+                 string:${view/getBaseURL}/Code/${attr/type_link}/index.html">
+            <code tal:content="attr/type" /></a>)
+        </tal:omit-tag>
+        <br/>
+        <i i18n:translate="">Value:</i>
+        <code tal:content="attr/value">u''</code><br />
+      </li>
+    
+    </ul>
+
+  </div>
+  </div>
+
+</body>
+</html>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/menu.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/menu.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/menu.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,34 @@
+<html metal:use-macro="views/apidoc_macros/menu">
+<body>
+
+  <div metal:fill-slot="menu" class="small">
+
+    <div tal:define="classes view/findClasses"
+        tal:condition="classes">
+
+        <a href="" target="main"
+           tal:repeat="info classes"
+           tal:attributes="href info/url"
+           tal:content="info/path">
+          /zope/app/Application
+        </a>
+    </div>
+
+    <div>
+      <span i18n:translate="">Class Finder:</span> <br/>
+      <i i18n:translate="">(Enter partial Python path)</i></div>
+    <form action="menu.html" method="post">
+      <input type="text" name="path" 
+             style="font-size: 80%; width=95%" />
+      <input type="submit" name="SUBMIT" value="Find" 
+             i18n:attributes="value find-button" style="font-size: 80%"/>
+    </form>
+
+    <p style="font-size: 120%">
+      <a href="./index.html" target="main" i18n:translate="">Browse
+        Zope Source</a>
+    </p>
+  </div>
+
+</body>
+</html>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/menu.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/menu.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/menu.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# 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 Module Menu
+
+$Id: browser.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.app import zapi
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.classregistry import classRegistry
+
+class Menu(object):
+    """Menu for the Class Documentation Module.
+
+    The menu allows for looking for classes by partial names. See
+    `findClasses()` for the simple search implementation.
+    """
+
+    def findClasses(self):
+        """Find the classes that match a partial path.
+
+        Examples::
+
+          >>> from zope.app.apidoc.tests import pprint
+          >>> from zope.app.apidoc.classmodule import Class
+          >>> cm = zapi.getUtility(IDocumentationModule, 'Class')
+          >>> mod = cm['zope']['app']['apidoc']['classmodule']['browser']
+
+          Setup a couple of classes and register them.
+
+          >>> class Foo(object):
+          ...     pass
+          >>> mod._Module__children['Foo'] = Class(mod, 'Foo', Foo)
+          >>> class Foo2(object):
+          ...     pass
+          >>> mod._Module__children['Foo2'] = Class(mod, 'Foo2', Foo2)
+          >>> class Blah(object):
+          ...     pass
+          >>> mod._Module__children['Blah'] = Class(mod, 'Blah', Blah)
+
+          Setup the view.
+
+          >>> from zope.publisher.browser import TestRequest
+          >>> menu = Menu()
+          >>> menu.context = None
+
+          Testing the method with various inputs.
+          
+          >>> menu.request = TestRequest(form={'path': 'Foo'})
+          >>> info = menu.findClasses()
+
+          >>> pprint(info)
+          [[('path', 'zope.app.apidoc.classmodule.browser.Foo'),
+            ('url',
+             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo')],
+           [('path', 'zope.app.apidoc.classmodule.browser.Foo2'),
+            ('url',
+             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo2')]]
+          
+          >>> menu.request = TestRequest(form={'path': 'o2'})
+          >>> info = menu.findClasses()
+          >>> pprint(info)
+          [[('path', 'zope.app.apidoc.classmodule.browser.Foo2'),
+            ('url',
+             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo2')]]
+          
+          >>> menu.request = TestRequest(form={'path': 'Blah'})
+          >>> info = menu.findClasses()
+          >>> pprint(info)
+          [[('path', 'zope.app.apidoc.classmodule.browser.Blah'),
+            ('url',
+             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Blah')]]
+        """
+        path = self.request.get('path', None)
+        if path is None:
+            return []
+        classModule = zapi.getUtility(IDocumentationModule, "Code")
+        results = []
+        for p in classRegistry.keys():
+            if p.find(path) >= 0:
+                klass = zapi.traverse(classModule, p.replace('.', '/'))
+                results.append(
+                    {'path': p,
+                     'url': zapi.absoluteURL(klass, self.request)
+                     })
+        results.sort(lambda x, y: cmp(x['path'], y['path']))
+        return results


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/module.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,142 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Module Views
+
+$Id: browser.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.interface.interface import InterfaceClass
+from zope.security.proxy import removeSecurityProxy
+from zope.proxy import removeAllProxies
+
+from zope.app import zapi
+from zope.app.i18n import ZopeMessageIDFactory as _
+
+from zope.app.apidoc.utilities import renderText, columnize
+from zope.app.apidoc.codemodule.module import Module
+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
+
+
+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>
+        """
+        text = self.context.getDocString()
+        if text is None:
+            return None
+        lines = text.strip().split('\n')
+        # Get rid of possible CVS id.
+        lines = [line for line in lines if not line.startswith('$Id')]
+        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')]]
+        """
+        entries = [{'name': name,
+                    'url': zapi.absoluteURL(obj, self.request),
+                    'ismodule': zapi.isinstance(obj, Module),
+                    'isinterface': zapi.isinstance(
+                         removeAllProxies(obj), InterfaceClass),
+                    'isclass': zapi.isinstance(obj, Class),
+                    'isfunction': zapi.isinstance(obj, Function),
+                    'istextfile': zapi.isinstance(obj, TextFile),
+                    'iszcmlfile': zapi.isinstance(obj, Configuration)}
+                   for name, obj in self.context.items()]
+        entries.sort(lambda x, y: cmp(x['name'], y['name']))
+        if columns:
+            entries = columnize(entries)
+        return entries
+
+    def getBreadCrumbs(self):
+        """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')]]
+        """
+        names = self.context.getPath().split('.') 
+        crumbs = []
+        module = self.context
+        # I really need the class here, so remove the proxy.
+        while removeSecurityProxy(module).__class__ is Module:
+            crumbs.append(
+                {'name': zapi.name(module),
+                 'url': zapi.absoluteURL(module, self.request)}
+                )
+            module = zapi.getParent(module)
+
+        crumbs.append(
+            {'name': _('[top]'),
+             'url': zapi.getMultiAdapter(
+                      (module, self.request), name='absolute_url')()} )
+
+        crumbs.reverse()
+        return crumbs


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/module_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,57 @@
+<html metal:use-macro="views/apidoc_macros/details">
+<body metal:fill-slot="contents">
+
+  <h1 class="details-header" i18n:translate="">Zope 3 Code Browser</h1>
+
+  <p class="breadcrumbs">
+    <span tal:repeat="entry view/getBreadCrumbs">
+      <a href="" 
+         tal:attributes="href string:${entry/url}/index.html"
+         tal:content="entry/name" />
+      <tal:omit-tag condition="not: repeat/entry/end">/</tal:omit-tag>
+    </span>
+  </p>
+
+  <div class="highlight"
+       tal:define="doc view/getDoc"
+       tal:condition="doc"
+       tal:content="structure doc">
+    Module Documentation
+  </div>
+
+
+  <table width="100%" valign="top"><tr>
+   
+  <td tal:repeat="column view/getEntries"><ul>
+    <li tal:repeat="entry column">
+      <a href=""
+         tal:condition="entry/ismodule"
+         tal:attributes="href string:./${entry/name}/index.html"
+         tal:content="entry/name" />
+      <a href=""
+         tal:condition="entry/isinterface"
+         tal:attributes="href string:./${entry/name}/apiindex.html"
+         tal:content="structure string:<b><i>${entry/name}</i></b>" />
+      <a href=""
+         tal:condition="entry/isclass"
+         tal:attributes="href string:./${entry/name}/index.html"
+         tal:content="structure string:<b>${entry/name}</b>" />
+      <a href=""
+         tal:condition="entry/isfunction"
+         tal:attributes="href string:./${entry/name}/index.html"
+         tal:content="structure string:<i>${entry/name}</i>" />
+      <a href=""
+         tal:condition="entry/iszcmlfile"
+         tal:attributes="href string:./${entry/name}/index.html"
+         tal:content="structure string:<i>${entry/name}</i>" />
+      <a href=""
+         tal:condition="entry/istextfile"
+         tal:attributes="href string:./${entry/name}/index.html"
+         tal:content="structure string:<i>${entry/name}</i>" />
+    </li>
+  </ul></td>
+
+  </tr></table>
+
+</body>
+</html>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/text.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/text.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,29 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Function Views
+
+$Id: browser.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.app.apidoc.utilities import renderText
+
+class TextFileDetails(object):
+    """Represents the details of the text file."""
+
+    def renderedContent(self):
+        if self.context.path.endswith('.stx'):
+            format = 'zope.source.stx'
+        else:
+            format = 'zope.source.rest'
+        return renderText(self.context.getContent(), format=format)


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/textfile_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,10 @@
+<html metal:use-macro="views/apidoc_macros/details">
+<body metal:fill-slot="contents">
+
+  <div class="documentation"
+       tal:content="structure view/renderedContent">
+    file content
+  </div>
+
+</body>
+</html>


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/zcml.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,126 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""ZCML Element Views
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.configuration.fields import GlobalObject, GlobalInterface
+from zope.interface import implements
+from zope.schema import getFieldNamesInOrder, getFieldsInOrder
+from zope.schema.interfaces import IFromUnicode
+from zope.security.proxy import removeSecurityProxy
+
+from zope.app import zapi
+from zope.app.tree.interfaces import IUniqueId
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.utilities import getPythonPath
+
+def findDocModule(obj):
+    if IDocumentationModule.providedBy(obj):
+        return obj
+    return findDocModule(zapi.getParent(obj))
+
+def _compareAttrs(x, y, nameOrder):
+    if x['name'] in nameOrder:
+        valueX = nameOrder.index(x['name'])
+    else:
+        valueX = 999999
+
+    if y['name'] in nameOrder:
+        valueY = nameOrder.index(y['name'])
+    else:
+        valueY = 999999
+
+    return cmp(valueX, valueY)
+        
+
+class DisplayComment(object):
+
+    value = property(lambda self: self.context.value)
+
+    id = property(lambda self: IUniqueId(self.context).getId())
+
+
+class DisplayDirective(object):
+
+    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 = ns.replace(':', '_co_')
+        ns = ns.replace('/', '_sl_')
+        zcml = zapi.getUtility(IDocumentationModule, 'ZCML')
+        return '%s/../ZCML/%s/%s/index.html' %(
+            zapi.absoluteURL(findDocModule(self), self.request), ns,
+            context.domElement.localName)
+        
+
+    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()]
+
+        for attr in attrs:
+            if name in schema:
+                field = schema[name]
+            elif name+'_' in schema:
+                field = schema[name+'_']
+            else:
+                continue
+
+            # 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 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 = [name.endswith('_') and name[:-1] or name
+                      for name in fieldNames]
+        attrs.sort(lambda x, y: _compareAttrs(x, y, fieldNames))
+
+        return attrs
+
+    def hasSubDirectives(self):
+        return len(self.context) != 0
+
+    def getElements(self):
+        context = removeSecurityProxy(self.context)
+        return context.values()


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser/zcmlfile_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcmlfile_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,47 @@
+<html>
+<head>
+  <style type="text/css" media="all">
+
+.directive {
+  font-family: monospace;  
+  padding: 1px;
+  margin: 2px 0px;
+}
+
+.tagName {
+  color: darkred;
+}
+
+.attributeName {
+  color: blue;
+}
+
+.attributeValue {
+  color: darkgreen;
+}
+
+.commentElement {
+  font-family: monospace;
+  font-style: italic;
+  padding: 1px;
+  margin: 2px 0px;
+  color: darkblue;
+}
+
+.editElement {
+  background-color: EEEEEE;
+}
+
+.required {
+  font-weight: bold;
+  font-style: italic;
+}
+
+  </style>
+</head>
+<body>
+
+  <div tal:replace="structure context/@@display" />
+
+</body>
+</html>
\ No newline at end of file


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

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,752 +0,0 @@
-##############################################################################
-#
-# 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.
-#
-##############################################################################
-"""Class Documentation Module Views
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-import re
-from types import TypeType, ClassType, FunctionType, ModuleType
-import xml.dom.minidom
-import xml.sax.saxutils
-
-from zope.configuration import docutils, xmlconfig
-from zope.configuration.config import ConfigurationContext
-from zope.configuration.fields import GlobalObject, Tokens
-from zope.app.traversing.interfaces import TraversalError
-from zope.interface.interface import InterfaceClass
-from zope.proxy import removeAllProxies
-from zope.schema import getFieldsInOrder
-from zope.security.proxy import removeSecurityProxy
-
-import zope.app
-from zope.app import zapi
-from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.app.apidoc.classmodule import Module, Class, Function, ZCMLFile
-from zope.app.apidoc.classmodule import classRegistry
-from zope.app.apidoc.interfaces import IDocumentationModule
-from zope.app.apidoc.utilities import getPythonPath, renderText, columnize
-from zope.app.apidoc.utilities import getPermissionIds, getFunctionSignature
-from zope.app.apidoc.utilities import getPublicAttributes
-from zope.app.apidoc.utilities import getInterfaceForAttribute
-from zope.app.apidoc.zcmlmodule import quoteNS
-
-
-class Menu(object):
-    """Menu for the Class Documentation Module.
-
-    The menu allows for looking for classes by partial names. See
-    `findClasses()` for the simple search implementation.
-    """
-
-    def findClasses(self):
-        """Find the classes that match a partial path.
-
-        Examples::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from zope.app.apidoc.classmodule import Class
-          >>> cm = zapi.getUtility(IDocumentationModule, 'Class')
-          >>> mod = cm['zope']['app']['apidoc']['classmodule']['browser']
-
-          Setup a couple of classes and register them.
-
-          >>> class Foo(object):
-          ...     pass
-          >>> mod._Module__children['Foo'] = Class(mod, 'Foo', Foo)
-          >>> class Foo2(object):
-          ...     pass
-          >>> mod._Module__children['Foo2'] = Class(mod, 'Foo2', Foo2)
-          >>> class Blah(object):
-          ...     pass
-          >>> mod._Module__children['Blah'] = Class(mod, 'Blah', Blah)
-
-          Setup the view.
-
-          >>> from zope.publisher.browser import TestRequest
-          >>> menu = Menu()
-          >>> menu.context = None
-
-          Testing the method with various inputs.
-          
-          >>> menu.request = TestRequest(form={'path': 'Foo'})
-          >>> info = menu.findClasses()
-
-          >>> pprint(info)
-          [[('path', 'zope.app.apidoc.classmodule.browser.Foo'),
-            ('url',
-             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo')],
-           [('path', 'zope.app.apidoc.classmodule.browser.Foo2'),
-            ('url',
-             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo2')]]
-          
-          >>> menu.request = TestRequest(form={'path': 'o2'})
-          >>> info = menu.findClasses()
-          >>> pprint(info)
-          [[('path', 'zope.app.apidoc.classmodule.browser.Foo2'),
-            ('url',
-             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo2')]]
-          
-          >>> menu.request = TestRequest(form={'path': 'Blah'})
-          >>> info = menu.findClasses()
-          >>> pprint(info)
-          [[('path', 'zope.app.apidoc.classmodule.browser.Blah'),
-            ('url',
-             'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Blah')]]
-        """
-        path = self.request.get('path', None)
-        if path is None:
-            return []
-        classModule = zapi.getUtility(IDocumentationModule, "Class")
-        results = []
-        for p in classRegistry.keys():
-            if p.find(path) >= 0:
-                klass = zapi.traverse(classModule, p.replace('.', '/'))
-                results.append(
-                    {'path': p,
-                     'url': zapi.absoluteURL(klass, self.request)
-                     })
-        results.sort(lambda x, y: cmp(x['path'], y['path']))
-        return results
-
-
-_obj_attr_re = r'(?P<start>&lt;%s.*?%s=".*?)(?P<value>%s)(?P<end>".*?&gt;)'
-_attrname_re = r'(?P<start>&lt;.*?%s.*?)(?P<attr>%s)(?P<end>=".*?".*?&gt;)'
-directives = {}
-
-class ZCMLFileDetails(ConfigurationContext):
-    """Represents the details of the ZCML file."""
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-        # TODO: This is not the best way to do this. We really need to revisit
-        # the entire implementation and move more to the ZCMLFile object.
-        package = removeSecurityProxy(
-            zapi.getParent(context))._Module__module
-        # Keep track of the package that is used for relative paths
-        self._package_stack = [package]
-        # Keep track of completed actions, so none is executed twice
-        self._done = []
-        # Kepp track of the parent node, so that we know whether we deal with
-        # a directive or sub-directive 
-        self._parent_node_info = None
-
-    # See zope.configuration.config.ConfigurationContext
-    # The package is used to resolve relative paths.
-    package = property(lambda self: self._package_stack[-1])
-
-    # All registered directives. The availability of directives depends on
-    # whether we a re looking for a directive or a sub-directive.
-    directives = property(lambda self: directives[self._parent_node_info])
-
-    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'
-        """
-        m = zapi.getUtility(IDocumentationModule, "Class")
-        return zapi.absoluteURL(zapi.getParent(m), self.request)
-
-    def getHTMLContents(self):
-        """Return an HTML markup version of the ZCML file with links to other
-        documentation modules."""
-        if not directives:
-            self._makeDocStructure()
-        dom = xml.dom.minidom.parse(self.context.path)
-        self.text = file(self.context.path, 'r').read()
-        self.text = xml.sax.saxutils.escape(self.text)
-        self.text = self.text.replace(' ', '&nbsp;')
-        self.text = self.text.replace('\n', '<br />\n')
-
-        # Link interface and other object references
-        for node in dom.childNodes:
-            self._linkObjectReferences(node)
-
-        # Link ZCML directives
-        for node in dom.childNodes:
-            self._linkDirectives(node)
-
-        # Color directive attributes
-        for node in dom.childNodes:
-            self._colorAttributeNames(node)
-
-        # Color comments
-        self._colorComments()
-        
-        return self.text
-
-    def _colorComments(self):
-        self.text = self.text.replace('&lt;!--',
-                                      '<span style="color: red">&lt;!--')
-        self.text = self.text.replace('--&gt;',
-                                      '--&gt;</span>')
-
-    def _colorAttributeNames(self, node):
-        if node.nodeType in (node.TEXT_NODE, node.COMMENT_NODE):
-            return
-
-        for attrName in node.attributes.keys():
-            disc = ('color attribute name', node.tagName, attrName)
-            if disc in self._done:
-                continue
-
-            expr = re.compile(_attrname_re %(node.tagName, attrName),
-                              re.DOTALL)
-            self.text = re.sub(
-                expr,
-                r'\1<span class="attribute">\2</span>\3',
-            self.text)
-            
-            self._done.append(disc)
-
-        for child in node.childNodes:
-            self._colorAttributeNames(child)
-
-
-    def _linkDirectives(self, node):
-        if node.nodeType in (node.TEXT_NODE, node.COMMENT_NODE):
-            return
-
-        if (node.tagName,) in self._done:
-            return
-
-        if node.namespaceURI in self.directives.keys() and \
-               node.localName in self.directives[node.namespaceURI].keys():
-            namespace = quoteNS(node.namespaceURI)
-        else:
-            namespace = 'ALL'
-
-        self.text = self.text.replace(
-            '&lt;'+node.tagName,
-            '&lt;<a class="tagname" href="%s/ZCML/%s/%s/index.html">%s</a>' %(
-            self.getBaseURL(), namespace, node.localName, node.tagName))
-
-        self.text = self.text.replace(
-            '&lt;/'+node.tagName+'&gt;',
-            '&lt;/<a class="tagname" '
-            'href="%s/ZCML/%s/%s/index.html">%s</a>&gt;' %(
-            self.getBaseURL(), namespace, node.localName, node.tagName))
-
-        self._done.append((node.tagName,))
-        
-        for child in node.childNodes:
-            self._linkDirectives(child)
-
-
-    def _linkObjectReferences(self, node):
-        if node.nodeType in (node.TEXT_NODE, node.COMMENT_NODE):
-            return
-
-        if node.localName == 'configure' and node.hasAttribute('package'):
-            self._package_stack.push(
-                self.resolve(node.getAttribute('package')))
-
-        for child in node.childNodes:
-            self._linkObjectReferences(child)
-
-        namespace = self.directives.get(node.namespaceURI, self.directives[''])
-        directive = namespace.get(node.localName)
-        if directive is None:
-            # Try global namespace
-            directive = self.directives[''].get(node.localName)
-
-        for name, field in getFieldsInOrder(directive[0]):
-            if node.hasAttribute(name.strip('_')):
-                self._evalField(field, node)
-
-        if node.localName == 'configure' and node.hasAttribute('package'):
-            self._package_stack.pop()
-
-
-    def _evalField(self, field, node, attrName=None, dottedName=None):
-        bound = field.bind(self)
-        if attrName is None:
-            attrName = field.getName().strip('_')
-        if dottedName is None:
-            dottedName = node.getAttribute(attrName)
-
-        if isinstance(field, Tokens) and \
-               isinstance(field.value_type, GlobalObject):
-                       
-            tokens = [token.strip()
-                      for token in node.getAttribute(attrName).split(' \n\t')
-                      if token.strip() != '']
-
-            for token in tokens:
-                self._evalField(field.value_type, node, attrName, dottedName)
-
-        if isinstance(field, GlobalObject):
-            obj = bound.fromUnicode(dottedName)
-            disc = (node.tagName, attrName, dottedName)
-
-            if disc in self._done:
-                return
-
-            if isinstance(obj, InterfaceClass):
-                expr = re.compile(_obj_attr_re %(
-                    node.tagName, attrName, dottedName.replace('.', r'\.')),
-                                  re.DOTALL)                
-                path = obj.__module__ + '.' + obj.__name__
-                self.text = re.sub(
-                    expr,
-                    r'\1<a class="objectref" '
-                    r'href="%s/Interface/%s/apiindex.html">\2</a>\3' %(
-                    self.getBaseURL(), path),
-                    self.text)
-
-            elif isinstance(obj, (TypeType, ClassType, FunctionType)):
-                expr = re.compile(_obj_attr_re %(
-                    node.tagName, attrName, dottedName.replace('.', r'\.')),
-                                  re.DOTALL)
-                path = (obj.__module__ + '.' + obj.__name__).replace('.', '/')
-                self.text = re.sub(
-                    expr,
-                    r'\1<a class="objectref" '
-                    r'href="%s/Class/%s/index.html">\2</a>\3' %(
-                    self.getBaseURL(), path),
-                    self.text)
-
-            elif isinstance(obj, ModuleType):
-                expr = re.compile(_obj_attr_re %(
-                    node.tagName, attrName, dottedName.replace('.', r'\.')),
-                                  re.DOTALL)
-                path = obj.__name__.replace('.', '/')
-                self.text = re.sub(
-                    expr,
-                    r'\1<a class="objectref" '
-                    r'href="%s/Class/%s/index.html">\2</a>\3' %(
-                    self.getBaseURL(), path),
-                    self.text)
-
-            self._done.append(disc)
-                        
-    def _makeDocStructure(self):
-        # Some trivial caching
-        global directives
-        context = xmlconfig.file(
-            zope.app.appsetup.appsetup.getConfigSource(),
-            execute=False)
-        namespaces, subdirs = docutils.makeDocStructures(context)
-
-        for ns_name, dirs in namespaces.items():
-            for dir_name, dir in dirs.items():
-                parent = directives.setdefault(None, {})
-                namespace = parent.setdefault(ns_name, {})
-                namespace[dir_name] = dir
-
-        for parent_info, dirs in subdirs.items():
-            for dir in dirs:
-                parent = directives.setdefault(parent_info, {})
-                namespace = parent.setdefault(dir[0], {})
-                namespace[dir[1]] = dir[2:]
-
-
-class FunctionDetails(object):
-    """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'
-        """
-        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')]
-        """
-        return [{'name': name,
-                 'value': `attr`,
-                 'type': type(attr).__name__,
-                 'type_link': _getTypePath(type(attr)).replace('.', '/')}
-                
-                for name, attr in self.context.getAttributes()]
-
-
-class ClassDetails(object):
-    """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'}]
-        """
-        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())
-          []
-        """
-        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'}]
-        """
-        info = []
-        classModule = zapi.getUtility(IDocumentationModule, "Class")
-        for cls in classes:
-            # We need to removeAllProxies because the security checkers for
-            # zope.app.container.contained.ContainedProxy and
-            # zope.app.i18n.messagecatalog.MessageCatalog prevent us from
-            # accessing __name__ and __module__.
-            unwrapped_cls = removeAllProxies(cls)
-            path = getPythonPath(unwrapped_cls)
-            try:
-                klass = zapi.traverse(classModule, path.replace('.', '/'))
-                url = zapi.absoluteURL(klass, self.request)
-            except TraversalError:
-                # If one of the classes is implemented in C, we will not
-                # be able to find it.
-                url = None
-            info.append({'path': path, 'url': url})
-        return info
-
-
-    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'
-        """
-        m = zapi.getUtility(IDocumentationModule, "Class")
-        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']
-        """
-        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)]
-        """
-        attrs = []
-        for name, attr, iface in self.context.getAttributes():
-            entry = {'name': name,
-                     'value': `attr`,
-                     'type': type(attr).__name__,
-                     'type_link': _getTypePath(type(attr)),
-                     'interface': getPythonPath(iface)}
-            # Since checkers do not have security declarations on their API,
-            # we have to remove the security wrapper to get to the API calls. 
-            checker = self.context.getSecurityChecker()
-            entry.update(
-                getPermissionIds(name, removeSecurityProxy(checker)))
-            attrs.append(entry)
-        return attrs
-
-
-    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)]]
-        """
-        methods = []
-        # remove the security proxy, so that `attr` is not proxied. We could
-        # unproxy `attr` for each turn, but that would be less efficient.
-        #
-        # `getPermissionIds()` also expects the class's security checker not
-        # to be proxied.
-        klass = removeSecurityProxy(self.context)
-        for name, attr, iface in klass.getMethods():
-            entry = {'name': name,
-                     'signature': getFunctionSignature(attr),
-                     'doc': renderText(attr.__doc__ or '',
-                                       zapi.getParent(self.context).getPath()),
-                     'interface': getPythonPath(iface)}
-            entry.update(getPermissionIds(name, klass.getSecurityChecker()))
-            methods.append(entry)
-        return methods
-
-
-    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>
-        """
-        return renderText(self.context.getDocString() or '',
-                          zapi.getParent(self.context).getPath())
-
-
-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>
-        """
-        text = self.context.getDocString()
-        if text is None:
-            return None
-        lines = text.strip().split('\n')
-        # Get rid of possible CVS id.
-        lines = [line for line in lines if not line.startswith('$Id')]
-        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')]]
-        """
-        entries = [{'name': name,
-                    'url': zapi.absoluteURL(obj, self.request),
-                    'ismodule': zapi.isinstance(obj, Module),
-                    'isclass': zapi.isinstance(obj, Class),
-                    'isfunction': zapi.isinstance(obj, Function),
-                    'iszcmlfile': zapi.isinstance(obj, ZCMLFile)}
-                   for name, obj in self.context.items()]
-        entries.sort(lambda x, y: cmp(x['name'], y['name']))
-        if columns:
-            entries = columnize(entries)
-        return entries
-
-    def getBreadCrumbs(self):
-        """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')]]
-        """
-        names = self.context.getPath().split('.') 
-        crumbs = []
-        module = self.context
-        # I really need the class here, so remove the proxy.
-        while removeSecurityProxy(module).__class__ is Module:
-            crumbs.append(
-                {'name': zapi.name(module),
-                 'url': zapi.absoluteURL(module, self.request)}
-                )
-            module = zapi.getParent(module)
-
-        crumbs.append(
-            {'name': _('[top]'),
-             'url': zapi.getMultiAdapter(
-                      (module, self.request), name='absolute_url')()} )
-
-        crumbs.reverse()
-        return crumbs
-
-
-def _getTypePath(type):
-    """Return the path of the type.
-
-    Here some examples::
-
-      >>> class Foo(object):
-      ...     pass
-
-      >>> _getTypePath(type(Foo()))
-      'zope.app.apidoc.classmodule.browser.Foo'
-
-      >>> _getTypePath(type(3))
-      '__builtin__.int'
-    """
-    path = getPythonPath(type)
-    return path

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/class_.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,190 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Class representation for code browser 
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+import inspect
+
+from zope.interface import implements, implementedBy
+from zope.security.checker import getCheckerForInstancesOf
+from zope.app.location.interfaces import ILocation
+
+from zope.app.apidoc.classregistry import classRegistry
+from zope.app.apidoc.utilities import getInterfaceForAttribute
+from zope.app.apidoc.utilities import getPublicAttributes
+from zope.app.apidoc.utilities import getPythonPath
+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)
+
+    """
+    implements(ILocation, IClassDocumentation)
+
+    def __init__(self, module, name, klass):
+        self.__parent__ = module
+        self.__name__ = name
+        self.__klass = klass
+
+        # Setup interfaces that are implemented by this class.
+        self.__interfaces = list(implementedBy(klass))
+        all_ifaces = {}
+        for iface in self.__interfaces:
+            all_ifaces[getPythonPath(iface)] = iface
+            for base in [base for base in iface.__bases__]:
+                all_ifaces[getPythonPath(base)] = base
+        self.__all_ifaces = all_ifaces.values()
+
+        # Register the class with the global class registry.
+        global classRegistry
+        classRegistry[self.getPath()] = klass
+
+    def getPath(self):
+        """See IClassDocumentation."""
+        return self.__parent__.getPath() + '.' + self.__name__
+
+    def getDocString(self):
+        """See IClassDocumentation."""
+        return self.__klass.__doc__
+
+    def getBases(self):
+        """See IClassDocumentation."""
+        return self.__klass.__bases__
+
+    def getKnownSubclasses(self):
+        """See IClassDocumentation."""
+        global classRegistry
+        return [k for n, k in classRegistry.getSubclassesOf(self.__klass)]
+
+    def getInterfaces(self):
+        """See IClassDocumentation."""
+        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>)]
+        """
+        return [
+            (name, getattr(self.__klass, name),
+             getInterfaceForAttribute(name, self.__all_ifaces, asPath=False))
+
+            for name in getPublicAttributes(self.__klass)
+            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>)]
+        """
+        return [
+            (name, getattr(self.__klass, name),
+             getInterfaceForAttribute(name, self.__all_ifaces, asPath=False))
+
+            for name in getPublicAttributes(self.__klass)
+            if inspect.ismethod(getattr(self.__klass, name))]
+
+    def getSecurityChecker(self):
+        """See IClassDocumentation."""
+        return getCheckerForInstancesOf(self.__klass)


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

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/class_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/class_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/class_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,175 +0,0 @@
-<html metal:use-macro="views/apidoc_macros/details">
-<body metal:fill-slot="contents">
-
-  <h1 class="details-header"
-      tal:content="context/getPath">
-    zope.app.Klass
-  </h1>
-
-  <div class="indent">
-    <div class="documentation" tal:content="structure view/getDoc">
-      Here is the doc string
-    </div>
-  </div>
-
-
-  <h2 class="details-section" i18n:translate="">Bases</h2>
-
-    <div class="indent"
-         tal:define="bases view/getBases">
-
-    <ul class="attr-list" tal:condition="bases">
-      <li tal:repeat="base bases">
-        <a href=""
-            tal:attributes="href string:${base/url}/index.html"
-            tal:content="base/path"
-            tal:condition="base/url" />
-        <div tal:condition="not: base/url">
-          <span tal:replace="base/path" />
-          <span i18n:translate="">(C-based class)</span>
-        </div>
-      </li>
-    </ul>
-
-    <p tal:condition="not: bases">
-      <em i18n:translate="">There are no base classes.</em>
-    </p>
-
-    </div>
-
-  <h2 class="details-section" i18n:translate="">Implemented Interfaces</h2>
-
-    <div class="indent"
-         tal:define="ifaces view/getInterfaces">
-
-    <ul class="attr-list" tal:condition="ifaces">
-      <li tal:repeat="iface ifaces">
-        <a href=""
-           tal:attributes="href
-               string:${view/getBaseURL}/Interface/$iface/apiindex.html"
-           tal:content="iface" />
-      </li>
-    </ul>
-
-    <p tal:condition="not: ifaces">
-      <em i18n:translate="">There are no implemented interfaces.</em>
-    </p>
-
-    </div>
-
-
-  <h2 class="details-section" i18n:translate="">Attributes/Properties</h2>
-
-  <div class="indent"
-      tal:define="attributes view/getAttributes">
-
-  <ul class="attr-list" tal:condition="attributes">
-
-    <li tal:repeat="attr attributes">
-      <b><code tal:content="attr/name">attr</code></b>
-      <tal:omit-tag condition="not: attr/type_link">
-        (type: <code tal:content="attr/type" />)
-      </tal:omit-tag>
-      <tal:omit-tag condition="attr/type_link">
-        (<span i18n:translate="">type:</span>
-        <a href=""
-           tal:attributes="href string:./index.html?path=${attr/type_link}">
-          <code tal:content="attr/type" /></a>)
-      </tal:omit-tag>
-      <br/>
-      <i i18n:translate="">Value:</i>
-      <code tal:content="attr/value">u''</code><br />
-
-      <span class="small" tal:condition="attr/interface">
-        <i i18n:translate="">Interface:</i>
-        <a href=""
-           tal:attributes="href
-          string:${view/getBaseURL}/Interface/${attr/interface}/apiindex.html"
-           tal:content="attr/interface">Iface</a><br />
-      </span>
-      <span class="small"
-          tal:condition="python: attr['read_perm'] and attr['write_perm']">
-        <i i18n:translate="">Permissions:</i>
-        <span tal:replace="attr/read_perm">zope.View</span>
-            <span i18n:translate="">(read)</span>,
-        <span tal:replace="attr/write_perm">zope.View</span>
-            <span i18n:translate="">(write)</span>
-      </span>
-    </li>
-
-  </ul>
-
-  <p tal:condition="not: attributes">
-    <em i18n:translate="">There are no attributes in this class.</em>
-  </p>
-
-  </div>
-
-
-  <h2 class="details-section" i18n:translate="">Methods</h2>
-
-  <div class="indent"
-      tal:define="methods view/getMethods">
-
-  <ul class="attr-list" tal:condition="methods">
-
-    <li tal:repeat="method view/getMethods">
-      <b><code
-          tal:content="string:${method/name}${method/signature}" />
-      </b><br>
-      <div class="inline-documentation" tal:content="structure method/doc">
-        method desc
-      </div>
-
-      <span class="small" tal:condition="method/interface">
-        <i i18n:translate="">Interface:</i>
-        <a href=""
-           tal:attributes="href
-          string:${view/getBaseURL}/Interface/${method/interface}/apiindex.html"
-           tal:content="method/interface">Iface</a><br/>
-      </span>
-
-      <span class="small"
-          tal:condition="python: method['read_perm'] and method['write_perm']">
-        <i i18n:translate="">Permissions:</i>
-        <span tal:replace="method/read_perm">zope.View</span>
-            <span i18n:translate="">(read)</span>,
-        <span tal:replace="method/write_perm">zope.View</span>
-            <span i18n:translate="">(write)</span>
-      </span>
-    </li>
-
-  </ul>
-
-  <p tal:condition="not: methods">
-    <em i18n:translate="">There are no methods in this class.</em>
-  </p>
-
-  </div>
-
-  <h2 class="details-section" i18n:translate="">Known Subclasses</h2>
-
-  <div class="indent"
-       tal:define="subclasses view/getKnownSubclasses">
-
-    <ul class="attr-list" tal:condition="subclasses">
-      <li tal:repeat="cls subclasses">
-        <a href=""
-            tal:attributes="href string:${cls/url}/index.html"
-            tal:content="cls/path"
-            tal:condition="cls/url" />
-        <div tal:condition="not: cls/url">
-          <span tal:replace="cls/path" />
-          <span i18n:translate="">(C-based class)</span>
-        </div>
-      </li>
-    </ul>
-
-    <p tal:condition="not: subclasses">
-      <em i18n:translate="">There are no known subclasses.</em>
-    </p>
-
-  </div>
-
-</body>
-</html>

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/configure.zcml	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/configure.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -3,25 +3,29 @@
     xmlns:apidoc="http://namespaces.zope.org/apidoc"
     xmlns:browser="http://namespaces.zope.org/browser">
 
-  <class class=".Module">
-    <allow interface=".IModuleDocumentation" />
+  <class class=".module.Module">
+    <allow interface=".interfaces.IModuleDocumentation" />
   </class>
 
-  <class class=".Class">
-    <allow interface=".IClassDocumentation" />
+  <class class=".class_.Class">
+    <allow interface=".interfaces.IClassDocumentation" />
   </class>
 
-  <class class=".Function">
-    <allow interface=".IFunctionDocumentation" />
+  <class class=".function.Function">
+    <allow interface=".interfaces.IFunctionDocumentation" />
   </class>
 
-  <class class=".ZCMLFile">
-    <allow interface=".IZCMLFileDocumentation" />
+  <class class=".zcml.Configuration">
+    <allow interface=".interfaces.IConfiguration" />
   </class>
 
-  <class class=".ClassModule">
-    <allow interface="zope.app.apidoc.interfaces.IDocumentationModule" />
-    <allow interface=".IModuleDocumentation" />
+  <class class=".text.TextFile">
+    <allow attributes="getContent path" />
+  </class>
+
+  <class class=".CodeModule">
+    <allow interface="..interfaces.IDocumentationModule" />
+    <allow interface=".interfaces.IModuleDocumentation" />
     <allow attributes="rootModules" />
   </class>
 
@@ -36,43 +40,10 @@
   <apidoc:rootModule module="zope" />
 
   <utility
-    provides="zope.app.apidoc.interfaces.IDocumentationModule"
-    factory=".ClassModule"
-    name="Class" />
+    provides="..interfaces.IDocumentationModule"
+    factory=".CodeModule"
+    name="Code" />
 
-  <browser:page
-    for=".ClassModule"
-    permission="zope.app.apidoc.UseAPIDoc"
-    class=".browser.Menu"
-    name="menu.html"
-    template="menu.pt" />
+  <include package=".browser" />
 
-  <browser:page
-    for=".IModuleDocumentation"
-    permission="zope.app.apidoc.UseAPIDoc"
-    class=".browser.ModuleDetails"
-    name="index.html"
-    template="module_index.pt" />
-
-  <browser:page
-    for=".IClassDocumentation"
-    permission="zope.app.apidoc.UseAPIDoc"
-    class=".browser.ClassDetails"
-    name="index.html"
-    template="class_index.pt" />
-
-  <browser:page
-    for=".IFunctionDocumentation"
-    permission="zope.app.apidoc.UseAPIDoc"
-    class=".browser.FunctionDetails"
-    name="index.html"
-    template="function_index.pt" />
-
-  <browser:page
-    for=".IZCMLFileDocumentation"
-    permission="zope.View"
-    class=".browser.ZCMLFileDetails"
-    name="index.html"
-    template="zcmlfile_index.pt" />
-
 </configure>

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/ftests.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/ftests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -22,63 +22,63 @@
     """Just a couple of tests ensuring that the templates render."""
 
     def testMenu(self):
-        response = self.publish('/++apidoc++/Class/menu.html', 
+        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++/Class/menu.html',
+        self.checkForBrokenLinks(body, '/++apidoc++/Code/menu.html',
                                  basic='mgr:mgrpw')
 
     def testMenuClassFinder(self):
-        response = self.publish('/++apidoc++/Class/menu.html',
+        response = self.publish('/++apidoc++/Code/menu.html',
                                 basic='mgr:mgrpw',
-                                form={'path': 'Class', 'SUBMIT': 'Find'})
+                                form={'path': 'Code', 'SUBMIT': 'Find'})
         self.assertEqual(response.getStatus(), 200)
         body = response.getBody()
-        self.assert_(body.find('zope.app.apidoc.classmodule.ClassModule') > 0)
-        self.checkForBrokenLinks(body, '/++apidoc++/Class/menu.html',
+        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++/Class/zope/app/apidoc/',
+        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++/Class/zope/app/apidoc/',
-                                 basic='mgr:mgrpw')
+        self.checkForBrokenLinks(
+            body, '/++apidoc++/Code/zope/app/apidoc/apidoc', basic='mgr:mgrpw')
 
     def testClassDetailsView(self):
         response = self.publish(
-            '/++apidoc++/Class/zope/app/apidoc/APIDocumentation',
+            '/++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++/Class/zope/app/apidoc/APIDocumentation',
+            body, '/++apidoc++/Code/zope/app/apidoc/apidoc/APIDocumentation',
             basic='mgr:mgrpw')
 
     def testFunctionDetailsView(self):
         response = self.publish(
-            '/++apidoc++/Class/zope/app/apidoc/handleNamespace',
+            '/++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++/Class/zope/app/apidoc/handleNamesapce',
+            body, '/++apidoc++/Code/zope/app/apidoc/apidoc/handleNamesapce',
             basic='mgr:mgrpw')
 
     def testZCMLFileDetailsView(self):
         response = self.publish(
-            '/++apidoc++/Class/zope/app/configure.zcml',
+            '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
             basic='mgr:mgrpw')
         self.assertEqual(response.getStatus(), 200)
         body = response.getBody()
         self.checkForBrokenLinks(
-            body, '/++apidoc++/Class/zope/app/configure.zcml',
+            body, '/++apidoc++/Code/zope/app/apidoc/configure.zcml/index.html',
             basic='mgr:mgrpw')
         
 

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/function.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/function.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/function.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Function representation for code browser 
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.interface import implements
+from zope.app.location.interfaces import ILocation
+
+from zope.app.apidoc.utilities import getFunctionSignature
+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()
+      []
+    """
+    implements(ILocation, IFunctionDocumentation)
+
+    def __init__(self, module, name, func):
+        self.__parent__ = module
+        self.__name__ = name
+        self.__func = func
+
+    def getPath(self):
+        """See IFunctionDocumentation."""
+        return self.__parent__.getPath() + '.' + self.__name__
+
+    def getDocString(self):
+        """See IFunctionDocumentation."""
+        return self.__func.__doc__
+
+    def getSignature(self):
+        """See IFunctionDocumentation."""
+        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')]
+        """
+        return self.__func.__dict__.items()


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

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/function_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/function_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/function_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,61 +0,0 @@
-<html metal:use-macro="views/apidoc_macros/details">
-<body metal:fill-slot="contents">
-
-  <h1 class="details-header"
-      tal:content="context/getPath">
-    zope.app.function
-  </h1>
-
-  <h2 class="details-section" i18n:translate="">Signature</h2>
-
-  <div class="indent">
-    <b><code
-        tal:content="string:${context/zope:name}${context/getSignature}" />
-    </b>
-  </div>
-
-  <div tal:condition="context/getDocString">
-    <h2 class="details-section" i18n:translate="">Documentation String</h2>
-
-    <div class="indent">
-      <div class="documentation" tal:content="structure view/getDocString">
-        Here is the doc string
-      </div>
-    </div>
-  </div>
-
-  <div 
-      tal:define="attributes view/getAttributes"      
-      tal:condition="attributes">
-
-  <h2 class="details-section" i18n:translate="">Attributes</h2>
-
-  <div class="indent">
-
-    <ul class="attr-list">
-    
-      <li tal:repeat="attr attributes">
-        <b><code tal:content="attr/name">attr</code></b>
-        <tal:omit-tag condition="not: attr/type_link">
-          (<span i18n:translate="">type:</span>
-            <code tal:content="attr/type" />)
-        </tal:omit-tag>
-        <tal:omit-tag condition="attr/type_link">
-          (<span i18n:translate="">type:</span>
-          <a href="" 
-             tal:attributes="href 
-                 string:${view/getBaseURL}/Class/${attr/type_link}/index.html">
-            <code tal:content="attr/type" /></a>)
-        </tal:omit-tag>
-        <br/>
-        <i i18n:translate="">Value:</i>
-        <code tal:content="attr/value">u''</code><br />
-      </li>
-    
-    </ul>
-
-  </div>
-  </div>
-
-</body>
-</html>

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/interfaces.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,240 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Interfaces for code browser
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+from zope.interface import Interface
+from zope.schema import Field, BytesLine, Text
+
+from zope.app.container.interfaces import IContainer
+from zope.app.container.interfaces import IReadContainer
+from zope.app.i18n import ZopeMessageIDFactory as _
+
+
+class IModuleDocumentation(IReadContainer):
+    """Representation of a Python module for documentation.
+
+    The items of the container are sub-modules and classes.
+    """
+    def getDocString():
+        """Return the doc string of the module."""
+
+    def getFileName():
+        """Return the file name of the module."""
+
+    def getPath():
+        """Return the Python path of the module."""
+
+
+class IClassDocumentation(Interface):
+    """Representation of a class or type for documentation."""
+
+    def getDocString():
+        """Return the doc string of the class."""
+
+    def getPath():
+        """Return the Python path of the class."""
+
+    def getBases():
+        """Return the base classes of the class."""
+
+    def getKnownSubclasses():
+        """Return the known subclasses classes of the class."""
+
+    def getInterfaces():
+        """Return the interfaces the class implements."""
+
+    def getAttributes():
+        """Return a list of 3-tuple attribute information.
+
+        The first entry of the 3-tuple is the name of the attribute, the
+        second is the attribute object itself. The third entry is the
+        interface in which the attribute is defined.
+
+        Note that only public attributes are returned, meaning only attributes
+        that do not start with an '_'-character.
+        """
+
+    def getMethods():
+        """Return a list of 3-tuple method information.
+
+        The first entry of the 3-tuple is the name of the method, the
+        second is the method object itself. The third entry is the
+        interface in which the method is defined.
+
+        Note that only public methods are returned, meaning only methods
+        that do not start with an '_'-character.
+        """
+
+    def getSecurityChecker():
+        """Return the security checker for this class.
+
+        Since 99% of the time we are dealing with name-based security
+        checkers, we can look up the get/set permission required for a
+        particular class attribute/method.
+        """
+
+
+class IFunctionDocumentation(Interface):
+    """Representation of a function for documentation."""
+
+    def getDocString():
+        """Return the doc string of the function."""
+
+    def getPath():
+        """Return the Python path of the function."""
+
+    def getSignature():
+        """Return the signature of the function as a string."""
+
+    def getAttributes():
+        """Return a list of 2-tuple attribute information.
+
+        The first entry of the 2-tuple is the name of the attribute, the
+        second is the attribute object itself.
+        """
+
+
+class IElement(IContainer):
+    """Represents an XML Element in the ZCML Configuration File
+
+    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.
+        
+        The returned XML should be well formatted, including all correct
+        indentations and lines not being long than 80 characters, if possible.
+        """
+
+    def getElementType():
+        """Return the type of the DOM element.
+        
+        Possible values are:
+
+        - ELEMENT_NODE (1): An XML tag. If an element is of this type it is a
+          standard ZCML directive or sub-directive.
+
+        - COMMENT_NODE (8): An XML comment. Comments are used to explain
+          features of the ZCML directives and are thus supported by the editor.
+
+        - 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.
+        """
+
+    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."""
+
+    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.
+
+        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(
+        title=_('Configuration Filename'),
+        description=_('Path to the configuration file'),
+        required=True)
+
+    package = BytesLine(
+        title=_('Configuration Package'),
+        description=_(
+        '''Specifies the package from which the configuration file will be
+        executed. If you do not specify the package, then the configuration
+        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():
+        """ """


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

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/menu.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/menu.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/menu.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,34 +0,0 @@
-<html metal:use-macro="views/apidoc_macros/menu">
-<body>
-
-  <div metal:fill-slot="menu" class="small">
-
-    <div tal:define="classes view/findClasses"
-        tal:condition="classes">
-
-        <a href="" target="main"
-           tal:repeat="info classes"
-           tal:attributes="href info/url"
-           tal:content="info/path">
-          /zope/app/Application
-        </a>
-    </div>
-
-    <div>
-      <span i18n:translate="">Class Finder:</span> <br/>
-      <i i18n:translate="">(Enter partial Python path)</i></div>
-    <form action="menu.html" method="post">
-      <input type="text" name="path" 
-             style="font-size: 80%; width=95%" />
-      <input type="submit" name="SUBMIT" value="Find" 
-             i18n:attributes="value find-button" style="font-size: 80%"/>
-    </form>
-
-    <p style="font-size: 120%">
-      <a href="./index.html" target="main" i18n:translate="">Browse
-        Zope Source</a>
-    </p>
-  </div>
-
-</body>
-</html>

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/metaconfigure.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/metaconfigure.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -18,7 +18,7 @@
 __docformat__ = 'restructuredtext'
 from zope.interface import implements
 from zope.app.component.metaconfigure import utility
-from zope.app.apidoc.classmodule import IAPIDocRootModule
+from zope.app.apidoc.codemodule import IAPIDocRootModule
 
 class RootModule(str):
     implements(IAPIDocRootModule)

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/module.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/module.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/module.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,200 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Module representation for code browser 
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+import os
+import types
+
+import zope
+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.apidoc.classregistry import safe_import
+from zope.app.apidoc.utilities import ReadContainerBase
+from interfaces import IModuleDocumentation
+
+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
+
+# 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']
+    """
+    implements(ILocation, IModuleDocumentation)
+
+    def __init__(self, parent, name, module, setup=True):
+        """Initialize object."""
+        self.__parent__ = parent
+        self.__name__ = name
+        self._module = module
+        self._children = {}
+        if setup:
+            self.__setup()
+
+    def __setup(self):
+        """Setup the module sub-tree."""
+        # Detect packages
+        if hasattr(self._module, '__file__') and \
+               (self._module.__file__.endswith('__init__.py') or
+                self._module.__file__.endswith('__init__.pyc')or
+                self._module.__file__.endswith('__init__.pyo')):
+            dir = os.path.dirname(self._module.__file__)
+            for file in os.listdir(dir):
+                if file in IGNORE_FILES:
+                    continue
+                path = os.path.join(dir, file)
+
+                if os.path.isdir(path) and '__init__.py' in os.listdir(path):
+                    fullname = self._module.__name__ + '.' + file
+                    module = safe_import(fullname)
+                    if module is not None:
+                        self._children[file] = Module(self, file, module)
+
+                elif os.path.isfile(path) and file.endswith('.py') and \
+                         not file.startswith('__init__'):
+                    name = file[:-3]
+                    fullname = self._module.__name__ + '.' + name
+                    module = safe_import(fullname)
+                    if module is not None:
+                        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)
+
+                elif os.path.isfile(path) and file.endswith('.txt'):
+                    self._children[file] = TextFile(path, file, self)
+
+        # Setup classes in module, if any are available.
+        zope.deprecation.__show__.off()
+        for name in self._module.__dict__.keys():
+            attr = getattr(self._module, name)
+            # We do not want to register duplicates or instances
+            if hasattr(attr, '__module__') and \
+                   attr.__module__ == self._module.__name__:
+
+                if not hasattr(attr, '__name__') or \
+                       attr.__name__ != name or \
+                       name.startswith('_'):
+                    continue
+
+                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)
+
+                elif type(attr) is types.FunctionType:
+                    self._children[name] = Function(self, name, attr)
+
+        zope.deprecation.__show__.on()
+
+
+    def getDocString(self):
+        """See IModule."""
+        return self._module.__doc__
+
+    def getFileName(self):
+        """See IModule."""
+        return self._module.__file__
+
+    def getPath(self):
+        """See IModule."""
+        return self._module.__name__
+
+    def get(self, key, default=None):
+        """See zope.app.container.interfaces.IReadContainer."""
+        obj = self._children.get(key, default)
+        if obj is not default:
+            return obj
+
+        # We are actually able to find much more than we promise
+        if self.getPath():
+            path = self.getPath() + '.' + key
+        else:
+            path = key
+        obj = safe_import(path)
+        if obj is not None:
+            return Module(self, key, obj)
+
+        return default
+
+    def items(self):
+        """See zope.app.container.interfaces.IReadContainer."""
+        return self._children.items()


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

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/module_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/module_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/module_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,49 +0,0 @@
-<html metal:use-macro="views/apidoc_macros/details">
-<body metal:fill-slot="contents">
-
-  <h1 class="details-header" i18n:translate="">Zope 3 Class Browser</h1>
-
-  <p class="breadcrumbs">
-    <span tal:repeat="entry view/getBreadCrumbs">
-      <a href="" 
-         tal:attributes="href string:${entry/url}/index.html"
-         tal:content="entry/name" />
-      <tal:omit-tag condition="not: repeat/entry/end">/</tal:omit-tag>
-    </span>
-  </p>
-
-  <div class="highlight"
-       tal:define="doc view/getDoc"
-       tal:condition="doc"
-       tal:content="structure doc">
-    Module Documentation
-  </div>
-
-
-  <table width="100%" valign="top"><tr>
-   
-  <td tal:repeat="column view/getEntries"><ul>
-    <li tal:repeat="entry column">
-      <a href=""
-         tal:condition="entry/ismodule"
-         tal:attributes="href string:./${entry/name}/index.html"
-         tal:content="entry/name" />
-      <a href=""
-         tal:condition="entry/isclass"
-         tal:attributes="href string:./${entry/name}/index.html"
-         tal:content="structure string:<b>${entry/name}</b>" />
-      <a href=""
-         tal:condition="entry/isfunction"
-         tal:attributes="href string:./${entry/name}/index.html"
-         tal:content="structure string:<i>${entry/name}</i>" />
-      <a href=""
-         tal:condition="entry/iszcmlfile"
-         tal:attributes="href string:./${entry/name}/index.html"
-         tal:content="structure string:<i>${entry/name}</i>" />
-    </li>
-  </ul></td>
-
-  </tr></table>
-
-</body>
-</html>

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/tests.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/tests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Tests for the Class Documentation Module
+"""Tests for the Code Documentation Module
 
 $Id$
 """
@@ -39,9 +39,10 @@
 from zope.app.traversing.adapters import RootPhysicallyLocatable
 from zope.app.traversing.adapters import Traverser
 
-from zope.app.apidoc.classmodule import ClassModule, IAPIDocRootModule
-from zope.app.apidoc.classmodule.browser import ClassDetails, ModuleDetails
-from zope.app.apidoc.classmodule.browser import FunctionDetails
+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
 
 
@@ -52,10 +53,10 @@
         implements(IAPIDocRootModule)
     ztapi.provideUtility(IAPIDocRootModule, RootModule('zope'), "zope")
 
-    module = ClassModule()
+    module = CodeModule()
     module.__name__ = ''
     directlyProvides(module, IContainmentRoot)
-    ztapi.provideUtility(IDocumentationModule, module, "Class")
+    ztapi.provideUtility(IDocumentationModule, module, "Code")
 
     ztapi.provideAdapter(
         None, ITraverser, Traverser)
@@ -86,25 +87,25 @@
 
 
 def getFunctionDetailsView():
-    cm = zapi.getUtility(IDocumentationModule, 'Class')
+    cm = zapi.getUtility(IDocumentationModule, 'Code')
     view = FunctionDetails()
-    view.context = zapi.traverse(cm, 'zope/app/apidoc/classmodule/tests/foo')
+    view.context = zapi.traverse(cm, 'zope/app/apidoc/codemodule/tests/foo')
     view.request = TestRequest()
     return view
 
 
 def getClassDetailsView():
-    cm = zapi.getUtility(IDocumentationModule, 'Class')
-    view = ClassDetails()
-    view.context = zapi.traverse(cm, 'zope/app/apidoc/classmodule/ClassModule')
+    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, 'Class')
+    cm = zapi.getUtility(IDocumentationModule, 'Code')
     view = ModuleDetails()
-    view.context = zapi.traverse(cm, 'zope/app/apidoc/classmodule')
+    view.context = zapi.traverse(cm, 'zope/app/apidoc/codemodule')
     view.request = TestRequest()
     return view
 
@@ -119,8 +120,8 @@
     
     def setUp(self):
         super(DirectivesTest, self).setUp()
-        import zope.app.apidoc.classmodule
-        self.context = xmlconfig.file('meta.zcml', zope.app.apidoc.classmodule)
+        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)
@@ -133,10 +134,11 @@
 
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.app.apidoc.classmodule.browser',
+        # 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),
-        DocTestSuite('zope.app.apidoc.classmodule',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
         unittest.makeSuite(DirectivesTest),
         ))
 

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/text.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/text.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/text.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,35 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Function representation for code browser 
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+from zope.interface import implements
+from zope.app.location.interfaces import ILocation
+
+class TextFile(object):
+    """This class represents a function declared in the module."""
+    implements(ILocation)
+
+    def __init__(self, path, name, package):
+        self.path = path
+        self.__parent__ = package
+        self.__name__ = name
+
+    def getContent(self):
+        file = open(self.path, 'r')
+        content = file.read()
+        file.close()
+        return content


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

Added: Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/zcml.py	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,246 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Configuration File Representation
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+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
+
+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
+
+context = None
+def getContext():
+    global context
+    if context is None:
+        context = xmlconfig.file(zope.app.appsetup.appsetup.getConfigSource(),
+                                 execute=False)
+    return context
+
+
+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 _getSubElement(self, dom, index):
+        """Helper method to create new element."""
+        element = Element(dom)
+        locate(element, self, unicode(index))
+        return element
+
+    def get(self, key, default=None):
+        """See zope.app.container.interfaces.IReadContainer"""        
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+    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]
+
+    def values(self):
+        """See zope.app.container.interfaces.IReadContainer"""        
+        return [self[key] for key in self.keys()]
+
+    def items(self):
+        """See zope.app.container.interfaces.IReadContainer"""        
+        return [(key, self[key]) for key in self.keys()]
+
+    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 __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):
+    """ """
+    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 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)]
+        
+
+class Configuration(Directive):
+    """Cofiguration Object"""
+    implements(IConfiguration)
+
+    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 parse(self):
+        """See configeditor.interfaces.IConfiguration"""
+        return minidom.parse(self.filename).getElementsByTagName('configure')[0]
+
+    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
+
+    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
+
+    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
+
+        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


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

Deleted: Zope3/trunk/src/zope/app/apidoc/codemodule/zcmlfile_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/zcmlfile_index.pt	2005-02-19 02:03:28 UTC (rev 29222)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/zcmlfile_index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,15 +0,0 @@
-<html metal:use-macro="views/apidoc_macros/details">
-<body metal:fill-slot="contents">
-
-  <h1 class="details-header"
-      tal:content="context/path">
-    zope.app.Klass
-  </h1>
-
-  <code class="zcml" tal:content="structure view/getHTMLContents">
-    <configure> </configure>
-  </code>
-
-
-</body>
-</html>

Added: Zope3/trunk/src/zope/app/apidoc/component.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/component.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/component.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,207 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Component Inspection Utilities
+
+$Id: browser.py 29199 2005-02-17 22:38:55Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+import types
+
+from zope.component.interfaces import IFactory
+from zope.component.site import AdapterRegistration, UtilityRegistration
+from zope.interface import Interface
+from zope.publisher.interfaces import IRequest
+
+from zope.app import zapi
+from zope.app.apidoc.utilities import getPythonPath, relativizePath
+from zope.app.apidoc.utilities import renderText
+from zope.app.apidoc.classregistry import classRegistry
+
+SPECIFIC_INTERFACE_LEVEL = 1
+EXTENDED_INTERFACE_LEVEL = 2
+GENERIC_INTERFACE_LEVEL = 4
+
+def getRequiredAdapters(iface, withViews=False):
+    """Get adapter registrations where the specified interface is required."""
+    gsm = zapi.getGlobalSiteManager()
+    for reg in gsm.registrations():
+        # Only get adapters
+        if not isinstance(reg, AdapterRegistration):
+            continue
+        # Ignore views
+        if not withViews and reg.required[-1] and \
+               reg.required[-1].isOrExtends(IRequest):
+            continue
+        # Only get the adapters for which this interface is required
+        for required_iface in reg.required:
+            if iface.isOrExtends(required_iface):
+                yield reg
+
+
+def getProvidedAdapters(iface, withViews=False):
+    """Get adapter registrations where this interface is provided."""
+    gsm = zapi.getGlobalSiteManager()
+    for reg in gsm.registrations():
+        # Only get adapters
+        if not isinstance(reg, AdapterRegistration):
+            continue
+        # Ignore views
+        if not withViews and reg.required[-1] and \
+               reg.required[-1].isOrExtends(IRequest):
+            continue
+        # Only get adapters for which this interface is provided
+        if not reg.provided.isOrExtends(iface):
+            continue
+        yield reg
+
+
+def filterAdapterRegistrations(regs, iface, level=SPECIFIC_INTERFACE_LEVEL):
+    """Return only those registrations that match the specifed level"""
+    for reg in regs:
+        if level & GENERIC_INTERFACE_LEVEL:
+            for required_iface in reg.required:
+                if required_iface in (Interface, None):
+                    yield reg
+                    continue
+
+        if level & EXTENDED_INTERFACE_LEVEL:
+            for required_iface in reg.required:
+                if required_iface is not Interface and \
+                       iface.extends(required_iface):
+                    yield reg
+                    continue
+            
+        if level & SPECIFIC_INTERFACE_LEVEL:
+            for required_iface in reg.required:
+                if required_iface is iface:
+                    yield reg
+                    continue
+
+
+def getClasses(iface):
+    """Get the classes that implement this interface."""
+    return classRegistry.getClassesThatImplement(iface)
+
+
+def getFactories(iface):
+    """Return the factory registrations, who will return objects providing this
+    interface."""
+    gsm = zapi.getGlobalSiteManager()
+    for reg in gsm.registrations():
+        if not isinstance(reg, UtilityRegistration):
+            continue
+        if reg.provided is not IFactory:
+            continue
+        interfaces = reg.component.getInterfaces()
+        try:
+            if interfaces.isOrExtends(iface):
+                yield reg
+        except AttributeError:
+            for interface in interfaces:
+                if interface.isOrExtends(iface):
+                    yield reg
+                    break
+
+
+def getUtilities(iface):
+    """Return all utility registrations that provide the interface."""
+    gsm = zapi.getGlobalSiteManager()
+    for reg in gsm.registrations():
+        if not isinstance(reg, UtilityRegistration):
+            continue
+        if reg.provided.isOrExtends(iface):
+            yield reg
+
+
+def getRealFactory(factory):
+    """Get the real factory.
+
+    Sometimes the original factory is masked by functions. If the function
+    keeps track of the original factory, use it.
+    """
+    if isinstance(factory, types.FunctionType) and hasattr(factory, 'factory'):
+        return factory.factory
+    return factory
+
+
+def getParserInfoInfoDictionary(info):
+    """Return a PT-friendly info dictionary for a parser info object."""
+    return {'file': relativizePath(info.file),
+            'url': relativizePath(info.file)[10:],
+            'line': info.line,
+            'eline': info.eline,
+            'column': info.column,
+            'ecolumn': info.ecolumn}
+
+
+def getInterfaceInfoDictionary(iface):
+    """Return a PT-friendly info dictionary for an interface."""
+    return {'module': iface.__module__, 'name': iface.getName()}
+    
+
+def getAdapterInfoDictionary(reg):
+    """Return a PT-friendly info dictionary for an adapter registration."""
+    factory = getRealFactory(reg.value)
+    path = getPythonPath(factory)
+    if isinstance(factory, types.MethodType):
+       url = None
+    else:
+        url = path.replace('.', '/')
+    if isinstance(reg.doc, (str, unicode)):
+        doc = reg.doc
+        zcml = None
+    else:
+        doc = None
+        zcml = getParserInfoInfoDictionary(reg.doc)
+
+    return {
+        'provided': getInterfaceInfoDictionary(reg.provided),
+        'required': [getInterfaceInfoDictionary(iface)
+                     for iface in reg.required
+                     if iface is not None],
+        'name': getattr(reg, 'name', None),
+        'factory': path,
+        'factory_url': url,
+        'doc': doc,
+        'zcml': zcml}
+
+
+def getFactoryInfoDictionary(reg):
+    """Return a PT-friendly info dictionary for a factory."""
+    factory = reg.component
+    if type(factory) in (types.ClassType, types.TypeType):
+        klass = factory
+    else:
+        klass = factory.__class__
+    path = getPythonPath(klass)
+
+    return {'name': reg.name or '<i>no name</i>',
+            'title': getattr(factory, 'title', u''),
+            'description': renderText(getattr(factory, 'description', u''),
+                                      module=klass.__module__),
+            'url': path.replace('.', '/')}
+
+
+def getUtilityInfoDictionary(reg):
+    """Return a PT-friendly info dictionary for a factory."""
+    if type(reg.component) in (types.ClassType, types.TypeType):
+        klass = reg.component
+    else:
+        klass = reg.component.__class__
+
+    path = getPythonPath(klass)
+    return {'name': reg.name or '<i>no name</i>',
+            'url_name': reg.name or '__noname__',
+            'path': path,
+            'url': path.replace('.', '/')}

Added: Zope3/trunk/src/zope/app/apidoc/component.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/component.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/component.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,265 @@
+==============================
+Component Inspection Utilities
+==============================
+
+Once you have an interface, you really want to discover on how this interface
+interacts with other components in Zope 3. The functions in
+
+  >>> from zope.app.apidoc import component
+ 
+provide you with utilities to make those discoveries. The functions are
+explained in detail in this document. Before we start though, we have to have
+some interfaces to work with:
+
+  >>> from zope.interface import Interface
+  >>> class IFoo(Interface):
+  ...     pass
+
+  >>> class IBar(Interface):
+  ...     pass
+
+  >>> class IFooBar(IFoo, IBar):
+  ...     pass
+
+  >>> class IResult(Interface):
+  ...     pass
+
+  >>> class ISpecialResult(IResult):
+  ...     pass
+
+
+`getRequiredAdapters(iface, withViews=False)`
+---------------------------------------------
+
+This function returns adapter registrations for adapters that require the
+specified interface. So let's create some adapter registrations:
+
+  >>> from zope.publisher.interfaces import IRequest
+  >>> from zope.app.testing import ztapi
+  >>> ztapi.provideAdapter((IFoo,), IResult, None)
+  >>> ztapi.provideAdapter((IFoo, IBar), ISpecialResult, None)
+  >>> ztapi.provideAdapter((IFoo, IRequest), ISpecialResult, None)
+
+  >>> regs = list(component.getRequiredAdapters(IFoo))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, ''), 
+   AdapterRegistration(('IFoo',), 'IResult', '', None, '')]
+
+Note how the adapter requiring an `IRequest` at the end of the required
+interfaces is neglected. This is because it is recognized as a view and views
+are not returned by default. But you can simply turn this flag on:
+
+  >>> regs = list(component.getRequiredAdapters(IFoo, withViews=True))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, ''), 
+   AdapterRegistration(('IFoo', 'IRequest'), 'ISpecialResult', '', None, ''), 
+   AdapterRegistration(('IFoo',), 'IResult', '', None, '')]
+
+The function will also pick up registrations that have required interfaces the
+specified interface extends:
+
+  >>> regs = list(component.getRequiredAdapters(IFoo))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, ''), 
+   AdapterRegistration(('IFoo',), 'IResult', '', None, '')]
+
+And all of the required interfaces are considered, of course:
+
+  >>> regs = list(component.getRequiredAdapters(IBar))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, '')]
+
+
+`getProvidedAdapters(iface, withViews=False)`
+---------------------------------------------
+
+Of course, we are also interested in the adapters that provide a certain
+interface. This function returns those adapter registrations, again ignoring
+views by default.
+
+  >>> regs = list(component.getProvidedAdapters(ISpecialResult))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, '')]
+
+And by specifying the `withView` flag, we get views as well:
+
+  >>> regs = list(component.getProvidedAdapters(ISpecialResult, withViews=True))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, ''),
+   AdapterRegistration(('IFoo', 'IRequest'), 'ISpecialResult', '', None, '')]
+
+We can of course also ask for adapters specifying `IResult`:
+
+  >>> regs = list(component.getProvidedAdapters(IResult, withViews=True))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBar'), 'ISpecialResult', '', None, ''),
+   AdapterRegistration(('IFoo', 'IRequest'), 'ISpecialResult', '', None, ''),
+   AdapterRegistration(('IFoo',), 'IResult', '', None, '')]
+
+
+`getClasses(iface)`
+-------------------
+
+This package comes with a little tool called the class registry
+(see `classregistry.txt`). It provides a dictionary of all classes in the
+visible packages. This function utilizes the registry to retrieve all classes
+that implement the specified interface.
+
+Let's start by creating and registering some classes:
+
+  >>> from zope.interface import implements
+  >>> from zope.app.apidoc.classregistry import classRegistry
+
+  >>> class MyFoo(object):
+  ...     implements(IFoo)
+  >>> classRegistry['MyFoo'] = MyFoo
+
+  >>> class MyBar(object):
+  ...     implements(IBar)
+  >>> classRegistry['MyBar'] = MyBar
+
+  >>> class MyFooBar(object):
+  ...     implements(IFooBar)
+  >>> classRegistry['MyFooBar'] = MyFooBar
+
+Let's now see whether what results we get:
+
+  >>> classes = component.getClasses(IFooBar)
+  >>> classes.sort()
+  >>> classes
+  [('MyFooBar', <class 'MyFooBar'>)]
+
+  >>> classes = component.getClasses(IFoo)
+  >>> classes.sort()
+  >>> classes
+  [('MyFoo', <class 'MyFoo'>), ('MyFooBar', <class 'MyFooBar'>)]
+
+
+`getFactories(ifaces)`
+----------------------
+
+Return the factory registrations of the factories that will return objects
+providing this interface.
+
+Again, the first step is to create some factories:
+
+  >>> from zope.component.factory import Factory
+  >>> from zope.component.interfaces import IFactory
+  >>> ztapi.provideUtility(IFactory, Factory(MyFoo), 'MyFoo')
+  >>> ztapi.provideUtility(IFactory, Factory(MyBar), 'MyBar')
+  >>> ztapi.provideUtility(IFactory, 
+  ...     Factory(MyFooBar, 'MyFooBar', 'My Foo Bar'), 'MyFooBar')
+
+Let's see whether we will be able to get them:
+
+  >>> regs = list(component.getFactories(IFooBar))
+  >>> regs.sort()
+  >>> regs
+  [UtilityRegistration('IFactory', 'MyFooBar', 
+                       <Factory for <class 'MyFooBar'>>, '')]
+
+  >>> regs = list(component.getFactories(IFoo))
+  >>> regs.sort()
+  >>> regs
+  [UtilityRegistration('IFactory', 'MyFoo', 
+                       <Factory for <class 'MyFoo'>>, ''),
+   UtilityRegistration('IFactory', 'MyFooBar', 
+                       <Factory for <class 'MyFooBar'>>, '')]
+
+
+`getUtilities(iface)`
+---------------------
+
+Return all utility registrations for utilities that provide the specified
+interface.
+
+As usual, we have to register some utilities first:
+
+  >>> ztapi.provideUtility(IFoo, MyFoo())
+  >>> ztapi.provideUtility(IBar, MyBar())
+  >>> ztapi.provideUtility(IFooBar, MyFooBar())
+
+Now let's have a look what we have:
+
+  >>> regs = list(component.getUtilities(IFooBar))
+  >>> regs.sort()
+  >>> regs #doctest:+ELLIPSIS
+  [UtilityRegistration('IFooBar', '', <MyFooBar object at ...>, '')]
+
+  >>> regs = list(component.getUtilities(IFoo))
+  >>> regs.sort()
+  >>> regs #doctest:+ELLIPSIS
+  [UtilityRegistration('IFoo', '', <MyFoo object at ...>, ''), 
+   UtilityRegistration('IFooBar', '', <MyFooBar object at ...>, '')]
+
+
+`getAdapterInfoDictionary(reg)`
+-------------------------------
+
+
+This function returns a page-template-friendly dictionary representing the
+data of an adapter registration in an output-friendly format.
+
+Let's first create an adapter registration:
+
+  >>> class MyResult(object):
+  ...     implements(IResult)
+
+  >>> from zope.component.site import AdapterRegistration
+  >>> reg = AdapterRegistration((IFoo, IBar), IResult, 'FooToResult',
+  ...                            MyResult, 'doc info')
+
+And now get the info dictionary:
+
+  >>> pprint(component.getAdapterInfoDictionary(reg))
+  {'doc': 'doc info',
+   'factory': '__builtin__.MyResult',
+   'factory_url': '__builtin__/MyResult',
+   'name': 'FooToResult',
+   'provided': {'module': '__builtin__',
+                'name': 'IResult'},
+   'required': [{'module': '__builtin__',
+                 'name': 'IFoo'},
+                {'module': '__builtin__',
+                 'name': 'IBar'}],
+   'zcml': None}
+
+`getFactoryInfoDictionary(reg)`
+-------------------------------
+
+This function returns a page-template-friendly dictionary representing the
+data of a factory (utility) registration in an output-friendly format.
+
+Luckily we have already registered some factories, so we just reuse their
+registrations:
+
+  >>> pprint(component.getFactoryInfoDictionary(
+  ...     component.getFactories(IFooBar).next()))
+  {'description': u'<div class="document">\nMy Foo Bar</div>\n',
+   'name': 'MyFooBar',
+   'title': 'MyFooBar',
+   'url': 'zope/component/factory/Factory'}
+
+
+`getUtilityInfoDictionary(name, factory)`
+-----------------------------------------
+
+This function returns a page-template-friendly dictionary representing the
+data of a utility registration in an output-friendly format.
+
+Luckily we have already registered some utilities, so we just reuse their
+registrations:
+
+  >>> pprint(component.getUtilityInfoDictionary(
+  ...     component.getUtilities(IFooBar).next()))
+  {'name': '<i>no name</i>',
+   'path': '__builtin__.MyFooBar',
+   'url': '__builtin__/MyFooBar',
+   'url_name': '__noname__'}
\ No newline at end of file

Modified: Zope3/trunk/src/zope/app/apidoc/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/configure.zcml	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/configure.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,5 +1,6 @@
 <configure
   xmlns="http://namespaces.zope.org/zope"
+  xmlns:apidoc="http://namespaces.zope.org/apidoc"
   i18n_domain="zope">
 
   <permission
@@ -7,7 +8,7 @@
       title="[zope.app.apidoc.UseAPIDoc] Access Online API documentation"
       />
 
-  <class class=".APIDocumentation">
+  <class class=".apidoc.APIDocumentation">
     <require
         interface="zope.app.container.interfaces.IReadContainer"
         permission="zope.ManageApplication"
@@ -17,23 +18,59 @@
   <view
       name="apidoc" type="*"
       provides="zope.app.traversing.interfaces.ITraversable" for="*"
-      factory=".apidocNamespace"
+      factory=".apidoc.apidocNamespace"
       />
   <adapter
       name="apidoc"
       provides="zope.app.traversing.interfaces.ITraversable" for="*"
-      factory=".apidocNamespace"
+      factory=".apidoc.apidocNamespace"
       />
 
+  <!-- Register Public API Dcoumentation -->
+
+  <apidoc:bookchapter 
+      id="inspect"
+      title="Inspection API"
+      />
+  <apidoc:bookchapter 
+      id="interface"
+      title="Interfaces"
+      doc_path="interface.txt"
+      parent="inspect"
+      />
+  <apidoc:bookchapter 
+      id="component"
+      title="Components"
+      doc_path="component.txt"
+      parent="inspect"
+      />
+  <apidoc:bookchapter 
+      id="presentation"
+      title="Presentation"
+      doc_path="presentation.txt"
+      parent="inspect"
+      />
+  <apidoc:bookchapter 
+      id="misc"
+      title="Miscellaneous"
+      doc_path="utilities.txt"
+      parent="inspect"
+      />
+  <apidoc:bookchapter 
+      id="classregistry"
+      title="Class Registry"
+      doc_path="classregistry.txt"
+      parent="inspect"
+      />
+
   <include package=".browser" />
 
   <!-- API Documentation Modules -->
   <include package=".bookmodule" />
-  <include package=".classmodule" />
+  <include package=".codemodule" />
   <include package=".ifacemodule" />
   <include package=".typemodule" />
   <include package=".utilitymodule" />
-  <include package=".viewmodule" />
   <include package=".zcmlmodule" />
 
 </configure>

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/README.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/README.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,42 @@
+==================================
+The Interface Documentation Module
+==================================
+
+This documentation module allows you to inspect all aspects of an interface
+and its role within the Zope 3 framework. The module can be instantiated like
+all other documentation modules:
+
+  >>> from zope.app.apidoc.ifacemodule.ifacemodule import InterfaceModule
+  >>> module = InterfaceModule()
+
+After registering an interface
+
+  >>> from zope.interface import Interface
+  >>> class IFoo(Interface):
+  ...     pass
+
+  >>> from zope.app.component.interface import provideInterface
+  >>> provideInterface(None, IFoo)
+  >>> provideInterface('IFoo', IFoo)
+
+Now let's lookup an interface that is registered.
+  
+  >>> module.get('IFoo')
+  <InterfaceClass __builtin__.IFoo>
+
+  >>> module.get('__builtin__.IFoo')
+  <InterfaceClass __builtin__.IFoo>
+  
+
+Now we find an interface that is not in the site manager, but exists.
+
+  >>> module.get('zope.app.apidoc.interfaces.IDocumentationModule')
+  <InterfaceClass zope.app.apidoc.interfaces.IDocumentationModule>
+ 
+Finally, you can list all registered interfaces:
+
+  >>> ifaces = module.items()
+  >>> ifaces.sort()
+  >>> pprint(ifaces)
+  [(u'IFoo', <InterfaceClass __builtin__.IFoo>),
+   (u'__builtin__.IFoo', <InterfaceClass __builtin__.IFoo>)]


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

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/__init__.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/__init__.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,113 +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.
-#
-##############################################################################
-"""Interface Documentation Module
-
-The interface documentation module retrieves its information from the
-site manager. Therefore, currently there are no unregsitered interfaces
-listed in the documentation. This might be good, since unregistered interfaces
-are usually private and not of interest to a general developer.
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-from zope.app import zapi
-from zope.interface import implements
-from zope.app.apidoc.interfaces import IDocumentationModule
-from zope.app.apidoc.utilities import ReadContainerBase
-from zope.app.location import LocationProxy
-from zope.app.component.interface \
-     import queryInterface, searchInterfaceUtilities
-from zope.app.i18n import ZopeMessageIDFactory as _
-
-class IInterfaceModule(IDocumentationModule):
-    """Interface API Documentation Module
-
-    This is a marker interface, so that we can write adapters for objects
-    implementing this interface.
-    """
-
-class InterfaceModule(ReadContainerBase):
-    r"""Represent the Documentation of all Interfaces.
-
-    This documentation is implemented using a simple `IReadContainer`. The
-    items of the container are all the interfaces listed in the closest
-    site manager and above.
-
-    Demonstration::
-
-      >>> module = InterfaceModule()
-
-      Lookup an interface that is registered.
-      
-      >>> module.get('IInterfaceModule').getName()
-      'IInterfaceModule'
-
-      >>> id = 'zope.app.apidoc.interfaces.IDocumentationModule'
-      >>> module.get(id).getName()
-      'IDocumentationModule'
-
-      Here we find an interface that is not in the site manager, but exists.
-
-      >>> module.get('zope.app.content.interfaces.IContentType').getName()
-      'IContentType'
-
-      >>> print '\n'.join([id for id, iface in module.items()])
-      IInterfaceModule
-      zope.app.apidoc.interfaces.IDocumentationModule
-    """
-
-    implements(IInterfaceModule)
-
-    # See zope.app.apidoc.interfaces.IDocumentationModule
-    title = _('Interfaces')
-
-    # See zope.app.apidoc.interfaces.IDocumentationModule
-    description = _("""
-    All used and important interfaces are registered through the site
-    manager. While it would be possible to just list all attributes, it is
-    hard on the user to read such an overfull list. Therefore, interfaces that
-    have partial common module paths are bound together.
-
-    The documentation of an interface also provides a wide variety of
-    information, including of course the declared attributes/fields and
-    methods, but also available adapters, and utilities that provide
-    this interface.
-    """)
-
-    def get(self, key, default=None):
-        """See zope.app.interfaces.container.IReadContainer"""
-        iface = queryInterface(key, default)
-        if iface is default: 
-            # Yeah, we find more items than we claim to have! This way we can
-            # handle all interfaces using this module. :-)
-            parts = key.split('.')
-            try:
-                mod = __import__('.'.join(parts[:-1]), {}, {}, ('*',))
-            except ImportError:
-                iface = default
-            else:
-                iface = getattr(mod, parts[-1], default)
-
-        if not iface is default:
-            iface = LocationProxy(iface, self, key)
-
-        return iface
-
-    def items(self):
-        """See zope.app.interfaces.container.IReadContainer"""
-        items = list(searchInterfaceUtilities(self))
-        items.sort()
-        items = [(i[0], LocationProxy(i[1], self, i[0])) for i in items]
-        return items
+# Make a package

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -16,211 +16,42 @@
 $Id$
 """
 __docformat__ = 'restructuredtext'
+from zope.interface import Interface
 
-from types import FunctionType, MethodType, ClassType, TypeType
-from zope.component.site import AdapterRegistration
-from zope.interface.declarations import providedBy
-from zope.interface.interfaces import IMethod, IInterface 
-from zope.proxy import removeAllProxies
 from zope.publisher.interfaces import IRequest
 from zope.publisher.interfaces.browser import ILayer
-from zope.schema.interfaces import IField
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
+from zope.publisher.interfaces.http import IHTTPRequest
+from zope.publisher.interfaces.ftp import IFTPRequest
 from zope.security.proxy import removeSecurityProxy
+from zope.proxy import removeAllProxies
 
 from zope.app import zapi
+from zope.app.publisher.browser import BrowserView
+
 from zope.app.apidoc.utilities import getPythonPath, renderText
-from zope.app.apidoc.classmodule import classRegistry
+from zope.app.apidoc.apidoc import APIDocumentation
+from zope.app.apidoc import classregistry
+from zope.app.apidoc import interface, component, presentation
 
-def _get(iface, type):
-    """Return a dictionary containing all the attributes in an interface.
+def findAPIDocumentationRoot(obj, request):
+    if zapi.isinstance(obj, APIDocumentation):
+        return zapi.absoluteURL(obj, request)
+    return findAPIDocumentationRoot(zapi.getParent(obj), request)
 
-    The type specifies whether we are looking for attributes or methods.
-
-    Example::
-
-      >>> from zope.interface import Interface, Attribute
-      >>> from zope.schema import Field
-
-      >>> class IFoo(Interface):
-      ...     foo = Field()
-      ...     bar = Field()
-      ...     def blah():
-      ...         pass
-
-      >>> _get(IFoo, IMethod).keys()
-      ['blah']
-
-      >>> names = _get(IFoo, IField).keys()
-      >>> names.sort()
-      >>> names
-      ['bar', 'foo']
-    """
-    # We really just want the attributes to be unproxied, since there are no
-    # security declarations for generic interface attributes, but we need to
-    # be able to get to the info.
-    # We remove the Proxy on the interface, so we save ourselves the work of
-    # removing it later individually from each attribute.
-    iface = removeAllProxies(iface)
-    items = {}
-    for name in iface:
-        attr = iface[name]
-        if type.providedBy(attr):
-            items[name] = attr
-    return items
-
-
-def _getInOrder(iface, type,
-                _itemsorter=lambda x, y: cmp(x[1].order, y[1].order)):
-    """Return a list of (name, value) tuples in native interface order.
-
-    The type specifies whether we are looking for attributes or methods. The
-    `_itemsorter` argument provides the function that is used to order the
-    fields. The default function should be the correct one for 99% of your
-    needs.
-
-    Example::
-
-      >>> from zope.interface import Interface, Attribute
-      >>> from zope.schema import Field
-
-      >>> class IFoo(Interface):
-      ...     foo = Field()
-      ...     bar = Field()
-      ...     def blah():
-      ...         pass
-
-      >>> [n for n, a in _getInOrder(IFoo, IMethod)]
-      ['blah']
-
-      >>> [n for n, a in _getInOrder(IFoo, IField)]
-      ['foo', 'bar']
-    """
-    items = _get(iface, type).items()
-    items.sort(_itemsorter)
-    return items
-
-
-def _getFieldInterface(field):
-    """Return PT-friendly dict about the field's interface.
-
-    Examples::
-
-      >>> from zope.app.apidoc.tests import pprint
-      >>> from zope.interface import implements, Interface
-      
-      >>> class IField(Interface):
-      ...     pass
-      >>> class ISpecialField(IField):
-      ...     pass
-      >>> class Field(object):
-      ...     implements(IField)
-      >>> class SpecialField(object):
-      ...     implements(ISpecialField)
-      >>> class ExtraField(SpecialField):
-      ...     pass
-
-      >>> info = _getFieldInterface(Field())
-      >>> pprint(info)
-      [('id', 'zope.app.apidoc.ifacemodule.browser.IField'),
-       ('name', 'IField')]
-
-      >>> info = _getFieldInterface(SpecialField())
-      >>> pprint(info)
-      [('id', 'zope.app.apidoc.ifacemodule.browser.ISpecialField'),
-       ('name', 'ISpecialField')]
-
-      >>> info = _getFieldInterface(ExtraField())
-      >>> pprint(info)
-      [('id', 'zope.app.apidoc.ifacemodule.browser.ISpecialField'),
-       ('name', 'ISpecialField')]
-    """
-    # This is bad, but due to bootstrapping, directlyProvidedBy does
-    # not work 
-    name = field.__class__.__name__
-    ifaces = list(providedBy(field))
-    # Usually fields have interfaces with the same name (with an 'I')
-    for iface in ifaces:
-        if iface.getName() == 'I' + name:
-            return {'name': iface.getName(), 'id': getPythonPath(iface)}
-    # Giving up...
-    return {'name': ifaces[0].getName(), 'id': getPythonPath(ifaces[0])}
-
-
-def _getFieldClass(field):
-    """Return PT-friendly dict about the field's class.
-
-    Examples::
-
-      >>> from zope.app.apidoc.tests import pprint
-      >>> from zope.interface import implements, Interface
-      
-      >>> class IField(Interface):
-      ...     pass
-      >>> class ISpecialField(IField):
-      ...     pass
-      >>> class Field(object):
-      ...     implements(IField)
-      >>> class SpecialField(object):
-      ...     implements(ISpecialField)
-      >>> class ExtraField(SpecialField):
-      ...     pass
-
-      >>> info = _getFieldClass(Field())
-      >>> pprint(info)
-      [('name', 'Field'),
-       ('path', 'zope/app/apidoc/ifacemodule/browser/Field')]
-
-      >>> info = _getFieldClass(SpecialField())
-      >>> pprint(info)
-      [('name', 'SpecialField'),
-       ('path', 'zope/app/apidoc/ifacemodule/browser/SpecialField')]
-
-      >>> info = _getFieldClass(ExtraField())
-      >>> pprint(info)
-      [('name', 'ExtraField'),
-       ('path', 'zope/app/apidoc/ifacemodule/browser/ExtraField')]
-    """
-    class_ = field.__class__
-    return {'name': class_.__name__,
-            'path': getPythonPath(class_).replace('.', '/')}
-
-
-def _getRequiredCSS(field):
-    """Return a CSS class name that represents whether the field is required.
-
-    Examples::
-
-      >>> class Field(object):
-      ...     required = False
-
-      >>> field = Field()
-      >>> _getRequiredCSS(field)
-      'optional'
-      >>> field.required = True
-      >>> _getRequiredCSS(field)
-      'required'
-
-    """
-    if field.required:
-        return 'required'
-    else:
-        return 'optional'
-
-
-def _getRealFactory(factory):
-    """Get the real factory.
-
-    Sometimes the original factory is masked by functions. If the function
-    keeps track of the original factory, use it.
-    """
-    if isinstance(factory, FunctionType) and hasattr(factory, 'factory'):
-        return factory.factory
-    return factory
-
-
-class InterfaceDetails(object):
+class InterfaceDetails(BrowserView):
     """View class for an Interface."""
 
+    def __init__(self, context, request):
+        super(InterfaceDetails, self).__init__(context, request)
+        self._prepareViews()
+        try:
+            self.apidocRoot = findAPIDocumentationRoot(context, request)
+        except TypeError:
+            # Probably context without location; it's a test
+            self.apidocRoot = ''
+
     def getId(self):
         """Return the id of the field as it is defined for the interface
         utility.
@@ -264,263 +95,97 @@
 
     def getTypes(self):
         """Return a list of interface types that are specified for this
-        interface.
-
-        Note that you should only expect one type at a time.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails, IFoo
-          >>> from zope.interface import Interface, directlyProvides
-          >>> class IType(Interface):
-          ...     pass
-
-          >>> details = getInterfaceDetails()
-          >>> details.getTypes()
-          []
-
-          >>> directlyProvides(IFoo, IType)
-          >>> type = details.getTypes()[0]
-          >>> pprint(type)
-          [('name', 'IType'),
-           ('path', 'zope.app.apidoc.ifacemodule.browser.IType')]
-
-          Cleanup
-
-          >>> directlyProvides(IFoo, [])
-        """
+        interface."""
         # We have to really, really remove all proxies, since self.context (an
         # interface) is usually security proxied and location proxied. To get
         # the types, we need all proxies gone, otherwise the proxies'
         # interfaces are picked up as well. 
-        context = removeAllProxies(self.context)
-        types = list(providedBy(context))
-        types.remove(IInterface)
+        iface = removeAllProxies(self.context)
         return [{'name': type.getName(),
                  'path': getPythonPath(type)}
-                for type in types]
+                for type in interface.getInterfaceTypes(iface)]
     
     def getAttributes(self):
-        r"""Return a list of attributes in the order they were specified.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
-
-          >>> attrs = details.getAttributes()
-          >>> pprint(attrs)
-          [[('doc', u'<p>This is bar.</p>\n'), ('name', 'bar')],
-           [('doc', u'<p>This is foo.</p>\n'), ('name', 'foo')]]
-        """
+        """Return a list of attributes in the order they were specified."""
         # The `Interface` and `Attribute` class have no security declarations,
         # so that we are not able to access any API methods on proxied
         # objects. If we only remove security proxies, the location proxy's
         # module will be returned.
         iface = removeAllProxies(self.context)
-        attrs = []
-        for name in iface:
-            attr = iface[name]
-            if not IMethod.providedBy(attr) and not IField.providedBy(attr):
-                attrs.append(attr)
-        return [{'name': attr.getName(),
-                 'doc': renderText(attr.getDoc() or '', iface.__module__)}
-                for attr in attrs]
+        return [interface.getAttributeInfoDictionary(attr)
+                for name, attr in interface.getAttributes(iface)]
 
     def getMethods(self):
-        r"""Return a list of methods in the order they were specified.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
-
-          >>> methods = details.getMethods()
-          >>> pprint(methods)
-          [[('doc', u'<p>This is blah.</p>\n'),
-            ('name', 'blah'),
-            ('signature', '()')],
-           [('doc', u'<p>This is get.</p>\n'),
-            ('name', 'get'),
-            ('signature', '(key, default=None)')]]
-        """        
+        """Return a list of methods in the order they were specified."""
         # The `Interface` class have no security declarations, so that we are
         # not able to access any API methods on proxied objects. If we only
         # remove security proxies, the location proxy's module will be
         # returned.
-        return [{'name': method.getName(),
-                 'signature': method.getSignatureString(),
-                 'doc': renderText(
-                            method.getDoc() or '',
-                            removeAllProxies(self.context).__module__)}
-                for method in _get(self.context, IMethod).values()]
-            
+        iface = removeAllProxies(self.context)
+        return [interface.getMethodInfoDictionary(method)
+                for name, method in interface.getMethods(iface)]
+
     def getFields(self):
         r"""Return a list of fields in required + alphabetical order.
 
         The required attributes are listed first, then the optional
-        attributes.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
-
-          >>> fields = details.getFields()
-          >>> pprint(fields)
-          [[('class',
-             [('name', 'TextLine'),
-              ('path', 'zope/schema/_bootstrapfields/TextLine')]),
-            ('default', "u'Foo'"),
-            ('description', u'<p>Title</p>\n'),
-            ('iface',
-             [('id', 'zope.schema.interfaces.ITextLine'),
-              ('name', 'ITextLine')]),
-            ('name', 'title'),
-            ('required', True),
-            ('required_css', 'required'),
-            ('title', u'')],
-           [('class',
-             [('name', 'Text'),
-              ('path', 'zope/schema/_bootstrapfields/Text')]),
-            ('default', "u'Foo.'"),
-            ('description', u'<p>Desc</p>\n'),
-            ('iface',
-             [('id', 'zope.schema.interfaces.IText'), ('name', 'IText')]),
-            ('name', 'description'),
-            ('required', False),
-            ('required_css', 'optional'),
-            ('title', u'')]]
-        """
+        attributes."""
         # The `Interface` class have no security declarations, so that we are
         # not able to access any API methods on proxied objects.  If we only
         # remove security proxies, the location proxy's module will be
         # returned.
-        _itemsorter = lambda x, y: cmp(
-            (x[1].required and '1' or '2', x[0].lower()),
-            (y[1].required and '1' or '2', y[0].lower()))
-        fields = map(lambda x: x[1], _getInOrder(
-            self.context, IField, _itemsorter=_itemsorter))
-        return [{'name': self._getFieldName(field),
-                 'iface': _getFieldInterface(field),
-                 'class': _getFieldClass(field),
-                 'required': field.required,
-                 'required_css': _getRequiredCSS(field),
-                 'default': repr(field.default),
-                 'title': field.title,
-                 'description': renderText(
-                     field.description or '',
-                     removeAllProxies(self.context).__module__)}
-                for field in fields]
+        iface = removeAllProxies(self.context)
+        # Make sure that the required fields are shown first
+        sorter = lambda x, y: cmp((not x[1].required, x[0].lower()),
+                                  (not y[1].required, y[0].lower()))
+        return [interface.getFieldInfoDictionary(field)
+                for name, field in interface.getFieldsInOrder(iface, sorter)]
 
-    def _getFieldName(self, field):
-        return field.getName()
+    def getSpecificRequiredAdapters(self):
+        """Get adapters where this interface is required."""
+        # Must remove security and location proxies, so that we have access to
+        # the API methods and class representation.
+        iface = removeAllProxies(self.context)
+        regs = component.getRequiredAdapters(iface)
+        regs = component.filterAdapterRegistrations(
+            regs, iface,
+            level=component.SPECIFIC_INTERFACE_LEVEL)
+        regs = [component.getAdapterInfoDictionary(reg)
+                  for reg in regs]
+        return regs
 
-    def getRequiredAdapters(self):
-        """Get adapters where this interface is required.
+    def getExtendedRequiredAdapters(self):
+        """Get adapters where this interface is required."""
+        # Must remove security and location proxies, so that we have access to
+        # the API methods and class representation.
+        iface = removeAllProxies(self.context)
+        regs = component.getRequiredAdapters(iface)
+        regs = component.filterAdapterRegistrations(
+            regs, iface,
+            level=component.EXTENDED_INTERFACE_LEVEL)
+        regs = [component.getAdapterInfoDictionary(reg)
+                  for reg in regs]
+        return regs
 
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
-
-          >>> adapters = details.getRequiredAdapters()
-          >>> adapters.sort()
-          >>> pprint(adapters[0])
-          [('factory',
-            'zope.app.location.traversing.LocationPhysicallyLocatable'),
-           ('factory_url',
-            'zope/app/location/traversing/LocationPhysicallyLocatable'),
-           ('name', ''),
-           ('provided',
-            'zope.app.traversing.interfaces.IPhysicallyLocatable'),
-           ('required', [])]
-        """
-        sm = zapi.getSiteManager()
-        # Must remove security proxies, so that we have access to the API
-        # methods. 
-        iface = removeSecurityProxy(self.context)
-        adapters = []
-        for reg in sm.registrations():
-            # Only grab adapters
-            if not isinstance(reg, AdapterRegistration):
-                continue
-            # Only grab the adapters for which this interface is required
-            if reg.required and reg.required[0] is not None and \
-                   iface not in reg.required:
-                continue
-            # Ignore views and layers
-            if IInterface.providedBy(reg.required[-1]) and (
-                   reg.required[-1].isOrExtends(IRequest) or
-                   reg.required[-1].isOrExtends(ILayer)):
-                continue
-            factory = _getRealFactory(reg.value)
-            path = getPythonPath(factory)
-            if type(factory) in (FunctionType, MethodType):
-               url = None
-            else:
-                url = path.replace('.', '/')
-            adapters.append({
-                'provided': getPythonPath(reg.provided),
-                'required': [getPythonPath(iface)
-                             for iface in reg.required
-                             if iface is not None],
-                'name': getattr(reg, 'name', None),
-                'factory': path,
-                'factory_url': url
-                })
-        return adapters
+    def getGenericRequiredAdapters(self):
+        """Get adapters where this interface is required."""
+        # Must remove security and location proxies, so that we have access to
+        # the API methods and class representation.
+        iface = removeAllProxies(self.context)
+        regs = component.getRequiredAdapters(iface)
+        regs = tuple(component.filterAdapterRegistrations(
+            regs, iface,
+            level=component.GENERIC_INTERFACE_LEVEL))
+        return [component.getAdapterInfoDictionary(reg)
+                for reg in regs]
         
     def getProvidedAdapters(self):
-        """Get adapters where this interface is provided.
-
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
-
-          >>> adapters = details.getProvidedAdapters()
-          >>> pprint(adapters)
-          [[('factory', '__builtin__.object'),
-            ('factory_url', '__builtin__/object'),
-            ('name', ''),
-            ('required', ['zope.app.apidoc.ifacemodule.tests.IBar'])]]
-        """
-        sm = zapi.getSiteManager()
+        """Get adapters where this interface is provided."""
         # Must remove security and location proxies, so that we have access to
         # the API methods and class representation.
-        iface = removeAllProxies(self.context)
-        adapters = []
-        for reg in sm.registrations():
-            # Only grab adapters
-            if not isinstance(reg, AdapterRegistration):
-                continue
-            # Only grab adapters for which this interface is provided
-            if iface is not reg.provided:
-                continue
-            # Ignore views
-            if IInterface.providedBy(reg.required[-1]) and \
-                   reg.required[-1].isOrExtends(IRequest):
-                continue
-            factory = _getRealFactory(reg.value)
-            path = getPythonPath(factory)
-            if type(factory) in (FunctionType, MethodType):
-               url = None
-            else:
-                url = path.replace('.', '/')
-            adapters.append({
-                'required': [getPythonPath(iface) for iface in reg.required],
-                'name': reg.name,
-                'factory': path,
-                'factory_url': url
-                })
-        return adapters
+        regs = component.getProvidedAdapters(removeAllProxies(self.context))
+        return [component.getAdapterInfoDictionary(reg)
+                for reg in regs]
 
     def getClasses(self):
         """Get the classes that implement this interface.
@@ -539,76 +204,53 @@
         # Must remove security and location proxies, so that we have access to
         # the API methods and class representation.
         iface = removeAllProxies(self.context)
-        classes = classRegistry.getClassesThatImplement(iface)
+        classes = classregistry.classRegistry.getClassesThatImplement(iface)
         return [{'path': path, 'url': path.replace('.', '/')}
                 for path, klass in classes]
 
     def getFactories(self):
         """Return the factories, who will provide objects implementing this
-        interface.
+        interface."""
+        # Must remove security and location proxies, so that we have access to
+        # the API methods and class representation.
+        regs = component.getFactories(removeAllProxies(self.context))
+        return [component.getFactoryInfoDictionary(reg)
+                for reg in regs]
 
-        Example::
-
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
-
-          >>> factories = details.getFactories()
-          >>> factories = [f.items() for f in factories]
-          >>> factories = [f for f in factories if f.sort() is None]
-          >>> factories.sort()
-          >>> pprint(factories)
-          [[('name', u'FooFactory'),
-            ('title', 'Foo Factory'),
-            ('url', 'zope/component/factory/Factory')]]
-        """
+    def getUtilities(self):
+        """Return all utilities that provide this interface."""
         # Must remove security and location proxies, so that we have access to
         # the API methods and class representation.
-        iface = removeAllProxies(self.context)
-        factories = [(n, f) for n, f in
-                    zapi.getFactoriesFor(iface)
-                    if iface in tuple(f.getInterfaces())]
-        info = []
-        for name, factory in factories:
-            if type(factory) in (ClassType, TypeType):
-                klass = factory
-            else:
-                klass = factory.__class__
-            path = getPythonPath(klass)
-            info.append({'name': name or '<i>no name</i>',
-                         'title': getattr(factory, 'title', ''),
-                         'url': path.replace('.', '/')})
-        return info
+        regs = component.getUtilities(removeAllProxies(self.context))
+        return [component.getUtilityInfoDictionary(reg)
+                for reg in regs]
 
-    def getUtilitiesFor(self):
-        """Return all utilities that provide this interface.
 
-        Example::
+    def _prepareViews(self):
+        self.httpViews = []
+        self.browserViews = []
+        self.ftpViews = []
+        self.xmlrpcViews = []
+        self.otherViews = []
 
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
-          >>> details = getInterfaceDetails()
+        for reg in presentation.getViews(removeAllProxies(self.context)):
+            type = presentation.getPresentationType(reg.required[-1])
+            info = presentation.getViewInfoDictionary(reg)
 
-          >>> utils = details.getUtilitiesFor()
-          >>> pprint(utils)
-          [[('name', u'The Foo'),
-            ('path', 'zope.app.apidoc.ifacemodule.tests.Foo'),
-            ('url', 'zope/app/apidoc/ifacemodule/tests/Foo'),
-            ('url_name', u'The Foo')]]
-        """
-        sm = zapi.getSiteManager()
-        # Must remove security and location proxies, so that we have access to
-        # the API methods and class representation.
-        utils = sm.getUtilitiesFor(removeAllProxies(self.context))
-        info = []
-        for name, util in utils:
-            if type(util) in (ClassType, TypeType):
-                klass = util
+            if type is IBrowserRequest:
+                self.browserViews.append(info)
+            elif type is IXMLRPCRequest:
+                self.xmlrpcViews.append(info)
+            elif type is IHTTPRequest:
+                self.httpViews.append(info)
+            elif type is IFTPRequest:
+                self.ftpViews.append(info)
             else:
-                klass = util.__class__
-            path = getPythonPath(klass)
-            info.append({'name': name or '<i>no name</i>',
-                         'url_name': name or '__noname__',
-                         'path': path,
-                         'url': path.replace('.', '/')})
-        return info
+                self.otherViews.append(info)
+
+        sort_function = lambda x, y: cmp(x['name'], y['name']) 
+        self.httpViews.sort(sort_function)
+        self.browserViews.sort(sort_function)
+        self.ftpViews.sort(sort_function)
+        self.xmlrpcViews.sort(sort_function)
+        self.otherViews.sort(sort_function)

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,353 @@
+======================================
+Module Menu and ZCML Directive Details
+======================================
+
+This document provides an overview of all browser-presentation related
+objects.
+
+`getAllTextOfInterface(iface)`
+------------------------------
+
+Get all searchable text from an interface
+
+  >>> import zope.interface
+  >>> import zope.schema
+  >>> class IFoo(zope.interface.Interface):
+  ...     '''foo'''
+  ...
+  ...     def bar(self):
+  ...         '''bar'''
+  ...
+  ...     blah = zope.interface.Attribute('blah', 'blah')
+  ...
+  ...     field = zope.schema.Field(
+  ...         title = u'title', description = u'description')
+
+Now get the text. Note that there is no particular order during the text
+collection.
+
+  >>> from zope.app.apidoc.ifacemodule.menu import getAllTextOfInterface
+  >>> text = getAllTextOfInterface(IFoo)
+  >>> u'foo' in text
+  True
+  >>> u'bar' in text
+  True
+  >>> u'blah' in text
+  True
+  >>> u'field' in text
+  True
+  >>> u'title' in text
+  True
+  >>> u'description' in text
+  True
+
+
+`Menu` class
+------------
+
+This is the menu class for the Interface Documentation Module.
+
+The menu allows one to look for interfaces by full-text search or partial
+names. The `findInterfaces()` function provides a simple search mechanism.
+
+Before we can test the method, let's create a `Menu` instance:
+
+  >>> from zope.interface.interfaces import IElement, IAttribute
+
+  >>> from zope.app.apidoc.ifacemodule.menu import Menu
+  >>> menu = Menu()
+  >>> menu.context = {'IElement': IElement, 'IAttribute': IAttribute}
+  >>> menu.request = {'name_only': 'on', 'search_str': ''}
+
+Now let's see how successful our searches are:
+
+  >>> menu.request['search_str'] = 'Elem'
+  >>> pprint(menu.findInterfaces())
+  [{'name': 'IElement',
+    'url': './IElement/apiindex.html'}]
+
+  >>> menu.request['search_str'] = 'I'
+  >>> pprint(menu.findInterfaces())
+  [{'name': 'IAttribute',
+    'url': './IAttribute/apiindex.html'},
+   {'name': 'IElement',
+    'url': './IElement/apiindex.html'}]
+
+Now using the full text search:
+
+  >>> del menu.request['name_only']
+
+  >>> menu.request['search_str'] = 'object'
+  >>> pprint(menu.findInterfaces())
+  [{'name': 'IAttribute',
+    'url': './IAttribute/apiindex.html'},
+   {'name': 'IElement',
+    'url': './IElement/apiindex.html'}]
+
+  >>> menu.request['search_str'] = 'Stores'
+  >>> pprint(menu.findInterfaces())
+  [{'name': 'IAttribute',
+    'url': './IAttribute/apiindex.html'}]
+
+
+`InterfaceDetails` class
+------------------------
+
+This view provides many details about an interface. Most methods of the class
+actually use the public inspection API.
+
+Before we can test the view, we need to create an interesting setup, so that
+the view can provide some useful data. Let's start by defining a complex
+interface:
+
+  >>> class IFoo(zope.interface.Interface):
+  ...     """This is the Foo interface
+  ... 
+  ...     More description here...
+  ...     """
+  ...     foo = zope.interface.Attribute('This is foo.')
+  ...     bar = zope.interface.Attribute('This is bar.')
+  ... 
+  ...     title = zope.schema.TextLine(
+  ...         description=u'Title', 
+  ...         required=True, 
+  ...         default=u'Foo')
+  ...
+  ...     description = zope.schema.Text(
+  ...         description=u'Desc', 
+  ...         required=False,
+  ...         default=u'Foo.')
+  ... 
+  ...     def blah():
+  ...         """This is blah."""
+  ...     
+  ...     def get(key, default=None):
+  ...         """This is get."""
+
+Let's now create another interface `IBar` and make `Foo` an adapter from
+`IBar` to `IFoo`:
+
+  >>> class IBar(zope.interface.Interface):
+  ...     pass
+
+  >>> class Foo(object):
+  ...     zope.interface.implements(IFoo)
+
+  >>> from zope.app.testing import ztapi
+  >>> ztapi.provideAdapter(IBar, IFoo, Foo)
+
+  >>> from zope.app.apidoc.classregistry import classRegistry
+  >>> classRegistry['__builtin__.Foo'] = Foo
+
+Let's also register a factory for `Foo`
+
+  >>> from zope.component.interfaces import IFactory
+  >>> from zope.component.factory import Factory
+  >>> ztapi.provideUtility(IFactory, Factory(Foo, title='Foo Factory'),
+  ...                      'FooFactory')
+
+and a utility providing `IFoo`:
+
+  >>> ztapi.provideUtility(IFoo, Foo(), 'The Foo')
+
+Now that the initial setup is done, we can create an interface that is located
+in the interface documentation module
+
+  >>> from zope.app.apidoc.ifacemodule.ifacemodule import InterfaceModule
+  >>> ifacemodule = InterfaceModule()
+
+  >>> from zope.app.apidoc.tests import Root
+  >>> ifacemodule.__parent__ = Root()
+  >>> ifacemodule.__name__ = 'Interfaces'
+
+  >>> from zope.app.location import LocationProxy
+  >>> iface = LocationProxy(IFoo, ifacemodule, 'IFoo')
+
+and finally the details view:
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> from zope.app.apidoc.ifacemodule.browser import InterfaceDetails
+  >>> details = InterfaceDetails(iface, TestRequest())
+
+
+`getId()`
+---------
+
+Return the id of the field as it is defined for the interface
+utility.
+
+  >>> details.getId()
+  'IFoo'
+
+`getDoc()`
+----------
+
+Return the main documentation string of the interface.
+
+  >>> details.getDoc()[:55]
+  u'<div class="document">\n<p>This is the Foo interface</p>'
+
+
+`getBases()`
+------------
+
+Get all bases of this class
+
+  >>> details.getBases()
+  ['zope.interface.Interface']
+
+
+`getTypes()`
+------------
+
+Return a list of interface types that are specified for this interface.
+
+Initially we do not have any types
+
+  >>> details.getTypes()
+  []
+
+but when I create and assign a type to the interface
+
+  >>> class IMyType(zope.interface.interfaces.IInterface):
+  ...     pass
+
+  >>> zope.interface.directlyProvides(IFoo, IMyType)
+
+we get a result:
+
+  >>> pprint(details.getTypes())
+  [{'name': 'IMyType',
+    'path': '__builtin__.IMyType'}]
+
+
+`getAttributes()`
+-----------------
+
+Return a list of attributes in the order they were specified.
+
+  >>> pprint(details.getAttributes())
+  [{'doc': u'<div class="document">\nThis is bar.</div>\n',
+    'name': 'bar'},
+   {'doc': u'<div class="document">\nThis is foo.</div>\n',
+    'name': 'foo'}]
+
+
+`getMethods()`
+--------------
+
+Return a list of methods in the order they were specified.
+
+  >>> pprint(details.getMethods())
+  [{'doc': u'<div class="document">\nThis is blah.</div>\n',
+    'name': 'blah',
+    'signature': '()'},
+   {'doc': u'<div class="document">\nThis is get.</div>\n',
+    'name': 'get',
+    'signature': '(key, default=None)'}]
+
+
+`getFields()`
+-------------
+
+Return a list of fields in required + alphabetical order.
+
+The required attributes are listed first, then the optional attributes.
+
+  >>> pprint(details.getFields())
+  [{'class': {'name': 'TextLine',
+              'path': 'zope/schema/_bootstrapfields/TextLine'},
+    'default': "u'Foo'",
+    'description': u'<div class="document">\nTitle</div>\n',
+    'iface': {'id': 'zope.schema.interfaces.ITextLine',
+              'name': 'ITextLine'},
+    'name': 'title',
+    'required': True,
+    'required_string': u'required',
+    'title': u''},
+   {'class': {'name': 'Text',
+              'path': 'zope/schema/_bootstrapfields/Text'},
+    'default': "u'Foo.'",
+    'description': u'<div class="document">\nDesc</div>\n',
+    'iface': {'id': 'zope.schema.interfaces.IText',
+              'name': 'IText'},
+    'name': 'description',
+    'required': False,
+    'required_string': u'optional',
+    'title': u''}]
+
+`getSpecificRequiredAdapters()`
+-------------------------------
+
+Get adapters where this interface is required.
+
+  >>> pprint(details.getSpecificRequiredAdapters())
+  []
+
+
+`getExtendedRequiredAdapters()`
+-------------------------------
+
+Get adapters where this interface is required.
+
+  >>> pprint(details.getExtendedRequiredAdapters())
+  []
+
+
+`getGenericRequiredAdapters()`
+------------------------------
+
+Get adapters where this interface is required.
+
+  >>> pprint(details.getGenericRequiredAdapters())
+  []
+
+
+`getProvidedAdapters()`
+-----------------------
+
+Get adapters where this interface is provided.
+
+  >>> pprint(details.getProvidedAdapters())
+  [{'doc': '',
+    'factory': '__builtin__.Foo',
+    'factory_url': '__builtin__/Foo',
+    'name': '',
+    'provided': {'module': '__builtin__',
+                 'name': 'IFoo'},
+    'required': [{'module': '__builtin__',
+                  'name': 'IBar'}],
+    'zcml': None}]
+
+
+`getClasses()`
+---------------
+
+Get the classes that implement this interface.
+
+  >>> pprint(details.getClasses())
+  [{'path': '__builtin__.Foo',
+    'url': '__builtin__/Foo'}]
+
+`getFactories()`
+----------------
+
+Return the factories, who will provide objects implementing this
+interface.
+
+  >>> pprint(details.getFactories())
+  [{'description': u'',
+    'name': 'FooFactory',
+    'title': 'Foo Factory',
+    'url': 'zope/component/factory/Factory'}]
+
+`getUtilities()`
+----------------
+
+Return all utilities that provide this interface.
+
+  >>> pprint(details.getUtilities())
+  [{'name': 'The Foo',
+    'path': '__builtin__.Foo',
+    'url': '__builtin__/Foo',
+    'url_name': 'The Foo'}]
\ No newline at end of file


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

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,100 @@
+<metal:block define-macro="ifacename"
+  ><span tal:replace="iface/module" />.<u tal:content="iface/name" 
+/></metal:block>
+
+<metal:block define-macro="zcml">
+  <a href=""
+      tal:attributes="href 
+         string:../../Code/${zcml/url}/index.html" 
+      tal:content="zcml/file">
+    zope/app/configure.zcml
+  </a>
+  <span class="small">(line <tal:block replace="zcml/line" />)</span>
+</metal:block>
+
+
+<metal:block define-macro="adapter">
+  <div>
+    <b><code>
+      <a href=""
+            tal:attributes="href
+                string:$rootURL/Code/${adapter/factory_url}/index.html" 
+            tal:content="adapter/factory" 
+            tal:condition="adapter/factory_url" />
+      <span tal:content="adapter/factory" 
+            tal:condition="not: adapter/factory_url" />
+    </code></b>
+    <span tal:condition="adapter/name">
+      (<span i18n:translate="">name:</span>
+        <tal:block content="adapter/name" />)
+    </span>
+  </div>
+
+  <div tal:condition="adapter/zcml" 
+       tal:define="zcml adapter/zcml">
+    <i i18n:translate="">registered:</i> 
+    <metal:block use-macro="context/@@interface_macros/zcml" />
+  </div>
+
+  <div>
+    <i i18n:translate="">requires:</i> 
+    <tal:block repeat="iface adapter/required">
+      <a href=""
+          tal:condition="iface"
+          tal:attributes="href 
+              string:../${iface/module}.${iface/name}/apiindex.html">
+        <metal:block use-macro="context/@@interface_macros/ifacename" 
+        /></a><tal:block condition="not:repeat/iface/end">, </tal:block>
+    </tal:block>
+    <span tal:condition="not:adapter/required" i18n:translate="">
+      No interface required.
+    </span>
+  </div>
+
+  <div tal:define="iface adapter/provided">
+    <i i18n:translate="">provides:</i> 
+    <a href=""
+        tal:condition="iface"
+        tal:attributes="href
+              string:../${iface/module}.${iface/name}/apiindex.html">
+      <metal:block use-macro="context/@@interface_macros/ifacename" />
+    </a>  
+  </div>
+
+  <div tal:condition="adapter/doc">
+    <i i18n:translate="">registration info:</i> 
+    <span tal:content="adapter/doc">reg info</span>
+  </div>
+
+</metal:block>
+
+
+<metal:block define-macro="factory">
+  <a href=""
+     tal:attributes="href string:../../Code/${factory/url}/"
+     tal:content="factory/name" />
+  <tal:block replace="string:(${factory/title})" condition="factory/title" />
+</metal:block>
+
+
+<metal:block define-macro="utility">
+  <a href=""
+     tal:attributes="href 
+         string:$rootURL/Utility/${view/getId}/${utility/url_name}/index.html"
+     tal:content="structure utility/name" />
+  <br />
+  <div><span i18n:translate="">Class:</span> <code>
+    <a href=""
+       tal:attributes="href string:$rootURL/Code/${utility/url}/index.html"
+       tal:content="utility/path" />
+  </code></div>              
+</metal:block>
+
+
+<metal:block define-macro="class">
+  <b><code>
+    <a href=""
+       tal:attributes="href string:$rootURL/Code/${klass/url}/index.html"
+       tal:content="klass/path" />
+  </code></b>
+</metal:block>

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -2,8 +2,8 @@
     xmlns="http://namespaces.zope.org/zope"
     xmlns:browser="http://namespaces.zope.org/browser">
 
-  <class class=".InterfaceModule">
-    <allow interface=".IInterfaceModule" />
+  <class class=".ifacemodule.InterfaceModule">
+    <allow interface=".ifacemodule.IInterfaceModule" />
     <allow interface="zope.app.container.interfaces.IReadContainer" />
   </class>
 
@@ -11,13 +11,44 @@
 
   <utility
       provides="zope.app.apidoc.interfaces.IDocumentationModule"
-      factory=".InterfaceModule"
+      factory=".ifacemodule.InterfaceModule"
       name="Interface" />
 
-  <!-- The name for the interface content cannot be 'index.html', since the 
-       introspector uses this name already. -->
+  <!-- Setup interface-related macros -->
 
   <browser:page
+      for="*"
+      name="interface_macros"
+      permission="zope.View"
+      class=".macros.InterfaceDetailsMacros"
+      allowed_interface="zope.interface.common.mapping.IItemMapping" 
+      />
+  
+  <browser:page
+      for="*"
+      name="iface_macros"
+      permission="zope.View"
+      template="iface_macros.pt"
+      />
+
+  <browser:page
+      for="*"
+      name="component_macros"
+      permission="zope.View"
+      template="component_macros.pt"
+      />
+
+  <browser:page
+      for="*"
+      name="presentation_macros"
+      permission="zope.View"
+      template="presentation_macros.pt"
+      />
+
+  <!-- The name for the interface content cannot be 'index.html', since -->
+  <!-- the introspector uses this name already. -->
+
+  <browser:page
       for="zope.interface.interfaces.IInterface"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.InterfaceDetails"
@@ -28,7 +59,7 @@
   <!-- Interface Documentation Module Menu -->
 
   <browser:page
-      for=".InterfaceModule"
+      for=".ifacemodule.InterfaceModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".menu.Menu"
       name="menu.html"

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/ftests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/ftests.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/ftests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -36,7 +36,7 @@
     def testInterfaceDetailsView(self):
         response = self.publish(
             '/++apidoc++/Interface'
-            '/zope.app.apidoc.ifacemodule.IInterfaceModule'
+            '/zope.app.apidoc.ifacemodule.ifacemodule.IInterfaceModule'
             '/apiindex.html',
             basic='mgr:mgrpw')
         self.assertEqual(response.getStatus(), 200)

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/iface_macros.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/iface_macros.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/iface_macros.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,85 @@
+<metal:block define-macro="attribute">
+  <b><code tal:content="attribute/name">attr</code></b>
+  <span i18n:translate="">(Attribute)</span>
+  <br />
+  <div class="inline-documentation" tal:content="structure attribute/doc">
+    attr desc
+  </div>
+</metal:block>
+
+
+<metal:block define-macro="field">
+  <b tal:attributes="class field/required_string">
+    <code tal:content="field/name">field</code>
+    <span tal:condition="field/required">*</span>
+  </b>
+  - 
+  <a href=""
+     tal:attributes="href string:$rootURL/Code/${field/class/path}/index.html">
+    <code tal:content="field/class/name">Field</code>
+  </a>
+
+  (<span i18n:translate="">default</span> =
+        <code tal:content="field/default" />)
+  <br />
+  <div tal:content="structure field/title" class="field-title">title</div>
+  <span tal:content="structure field/description">field desc</span>
+</metal:block>
+
+
+<metal:block define-macro="method">
+  <b><code 
+      tal:content="string:${method/name}${method/signature}" />
+  </b><br>
+  <div class="inline-documentation" tal:content="structure method/doc">
+    method desc
+  </div>
+</metal:block>
+
+
+<metal:block define-macro="methods">
+  <div metal:define-slot="header">
+    <h2 class="details-section" i18n:translate="">Methods</h2>
+  </div>
+  <div class="indent">
+
+    <ul class="attr-list" tal:condition="methods">
+      <li tal:repeat="method methods">
+        <metal:block use-macro="context/@@interface_macros/method" />
+      </li>
+    </ul>
+
+    <p tal:condition="not: methods">
+      <em i18n:translate="">There are no methods specified.</em>
+    </p>
+
+  </div>
+</metal:block>
+
+<metal:block define-macro="attributes_fields">
+  <div metal:define-slot="header">
+    <h2 class="details-section" i18n:translate="">Attributes/Fields</h2>
+  </div>
+  <div class="indent">
+
+    <ul class="attr-list" 
+        tal:condition="python: attributes or fields">
+      <li tal:repeat="attribute attributes">
+        <metal:block use-macro="context/@@interface_macros/attribute" />
+      </li>
+      <li tal:repeat="field fields">
+        <metal:block use-macro="context/@@interface_macros/field" />
+      </li>
+    </ul>
+    
+    <p tal:condition="python: not (attributes or fields)">
+      <em i18n:translate="">There are no attributes or fields specified.</em>
+    </p>
+    
+    <p tal:condition="python: attributes or fields">
+      <em><b class="required">*</b> =
+      <span i18n:translate="">required</span></em>
+    </p>
+
+  </div>
+</metal:block>

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/ifacemodule.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/ifacemodule.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/ifacemodule.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,90 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Interface Documentation Module
+
+The interface documentation module retrieves its information from the
+site manager. Therefore, currently there are no unregsitered interfaces
+listed in the documentation. This might be good, since unregistered interfaces
+are usually private and not of interest to a general developer.
+
+$Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.app import zapi
+from zope.interface import implements
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.utilities import ReadContainerBase
+from zope.app.location import LocationProxy
+from zope.app.component.interface \
+     import queryInterface, searchInterfaceUtilities
+from zope.app.i18n import ZopeMessageIDFactory as _
+
+class IInterfaceModule(IDocumentationModule):
+    """Interface API Documentation Module
+
+    This is a marker interface, so that we can write adapters for objects
+    implementing this interface.
+    """
+
+class InterfaceModule(ReadContainerBase):
+    r"""Represent the Documentation of all Interfaces.
+
+    This documentation is implemented using a simple `IReadContainer`. The
+    items of the container are all the interfaces listed in the global
+    site manager."""
+
+    implements(IInterfaceModule)
+
+    # See zope.app.apidoc.interfaces.IDocumentationModule
+    title = _('Interfaces')
+
+    # See zope.app.apidoc.interfaces.IDocumentationModule
+    description = _("""
+    All used and important interfaces are registered through the site
+    manager. While it would be possible to just list all attributes, it is
+    hard on the user to read such an overfull list. Therefore, interfaces that
+    have partial common module paths are bound together.
+
+    The documentation of an interface also provides a wide variety of
+    information, including of course the declared attributes/fields and
+    methods, but also available adapters, and utilities that provide
+    this interface.
+    """)
+
+    def get(self, key, default=None):
+        """See zope.app.interfaces.container.IReadContainer"""
+        iface = queryInterface(key, default)
+        if iface is default: 
+            # Yeah, we find more items than we claim to have! This way we can
+            # handle all interfaces using this module. :-)
+            parts = key.split('.')
+            try:
+                mod = __import__('.'.join(parts[:-1]), {}, {}, ('*',))
+            except ImportError:
+                iface = default
+            else:
+                iface = getattr(mod, parts[-1], default)
+
+        if not iface is default:
+            iface = LocationProxy(iface, self, key)
+
+        return iface
+
+    def items(self):
+        """See zope.app.interfaces.container.IReadContainer"""
+        items = list(searchInterfaceUtilities(self))
+        items.sort()
+        items = [(i[0], LocationProxy(i[1], self, i[0])) for i in items]
+        return items


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

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/index.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,5 +1,6 @@
-<html metal:use-macro="views/apidoc_macros/details">
-<body metal:fill-slot="contents">
+<html metal:use-macro="context/@@apidoc_macros/details">
+<body metal:fill-slot="contents"
+      tal:define="rootURL view/apidocRoot">
 
   <h1 class="details-header" tal:content="view/getId">
     zope.app.interfaces.IInterface
@@ -37,165 +38,157 @@
     </ul>
     
     <p tal:condition="not: bases">
-      <em i18n:translate="">There are no base classes.</em>
+      <em i18n:translate="">There are no base interfaces.</em>
     </p>
     
   </div>
 
 
-  <h2 class="details-section" i18n:translate="">Attributes/Fields</h2>
+  <tal:block define="attributes view/getAttributes;
+                     fields view/getFields">
+    <metal:block use-macro="context/@@interface_macros/attributes_fields" />
+  </tal:block>
 
-  <div class="indent"
-      tal:define="attributes view/getAttributes;
-                  fields view/getFields">
+  <tal:block define="methods view/getMethods">
+    <metal:block use-macro="context/@@interface_macros/methods" />
+  </tal:block>
 
-  <ul class="attr-list" 
-      tal:condition="python: attributes or fields">
+  <h2 class="details-section" i18n:translate="">Adapters</h2>
   
-    <li tal:repeat="attr attributes">
-      <b><code tal:content="attr/name">attr</code></b>
-        <span i18n:translate="">(Attribute)</span><br>
-      <div class="inline-documentation" tal:content="structure attr/doc">
-        attr desc
-      </div>      
-    </li>
+  <div tal:define="specific_adapters view/getSpecificRequiredAdapters;
+                   extended_adapters view/getExtendedRequiredAdapters;
+                   generic_adapters view/getGenericRequiredAdapters;
+                   provided_adapters view/getProvidedAdapters">
 
-    <li tal:repeat="field fields">
-      <b tal:attributes="class field/required_css">
-        <code tal:content="field/name">field</code>
-        <span tal:condition="field/required">*</span>
-      </b>
-      - <a href=""
-           tal:attributes="
-             href string:../../Class/${field/class/path}/index.html">
-          <code tal:content="field/class/name">Field</code></a>
-      (<span i18n:translate="">default</span> =
-            <code tal:content="field/default" />)<br />
-      <div tal:content="structure field/title" class="field-title">title</div>
-      <span tal:content="structure field/description">field desc</span>      
-    </li>
-
-  </ul>
-
-  <p tal:condition="python: not (attributes or fields)">
-    <em i18n:translate="">There are no attributes or fields specified.</em>
-  </p>
-
+    <div class="indent" 
+         tal:condition="python: specific_adapters or generic_adapters">
+    
+      <h3 i18n:translate="">Adapters where this interface is required:</h3>
+    
+      <h4 i18n:translate="">Specific Adapters</h4>
+      <ul class="attr-list" 
+          tal:condition="specific_adapters">
+        <li tal:repeat="adapter specific_adapters">
+          <metal:block use-macro="context/@@interface_macros/adapter" />
+        </li>
+      </ul>
+      <p tal:condition="not:specific_adapters">
+        <em i18n:translate="">There are no specific adapters registered for
+            this interface.</em>
+      </p>
+    
+      <h4 i18n:translate="">Extended Adapters</h4>
+      <ul class="attr-list" 
+          tal:condition="extended_adapters">
+        <li tal:repeat="adapter extended_adapters">
+          <metal:block use-macro="context/@@interface_macros/adapter" />
+        </li>
+      </ul>
+      <p tal:condition="not:extended_adapters">
+        <em i18n:translate="">There are no extended adapters registered for
+            this interface.</em>
+      </p>
+    
+      <h4 i18n:translate="">Generic Adapters</h4>
+      <ul class="attr-list"
+          tal:condition="generic_adapters">
+        <li tal:repeat="adapter generic_adapters">
+          <metal:block use-macro="context/@@interface_macros/adapter" />
+        </li>
+      </ul>
+      <p tal:condition="not:generic_adapters">
+        <em i18n:translate="">There are no generic adapters registered.</em>
+      </p>
+    
+    </div>
+    
+    <div class="indent" 
+         tal:condition="provided_adapters">
+    
+      <h3 i18n:translate="">Adapters that provide this interface:</h3>
+      <ul>
+        <li tal:repeat="adapter provided_adapters">
+          <metal:block use-macro="context/@@interface_macros/adapter" />
+        </li>
+      </ul>
+    
+    </div>
+    
+    <p tal:condition="
+       python: not (specific_adapters or extended_adapters or generic_adapters
+                    or provided_adapters)">
+      <em i18n:translate="">There are no adapters registered for
+          this interface.</em>
+    </p>
   </div>
 
+  <div tal:define="Views view/browserViews"
+       tal:condition="Views">
 
+    <h2 class="details-section" i18n:translate="">Views</h2>
 
-  <h2 class="details-section" i18n:translate="">Methods</h2>
+    <div class="indent">
 
-  <div class="indent">
-
-  <ul class="attr-list" tal:condition="view/getMethods">
-    <li tal:repeat="method view/getMethods">
-      <b><code 
-          tal:content="string:${method/name}${method/signature}" />
-      </b><br>
-      <div class="inline-documentation" tal:content="structure method/doc">
-        method desc
-      </div>      
-    </li>
-  </ul>
-
-  <p tal:condition="not: view/getMethods">
-    <em i18n:translate="">There are no methods specified.</em>
-  </p>
-
-  </div>
-
-
-
-  <h2 class="details-section" i18n:translate="">Adapters</h2>
+      <tal:block condition="view/browserViews">
+        <h3 class="details-section" i18n:translate="">Browser</h3>
+        <div class="indent">    
+          <ul class="attr-list">
+            <li tal:repeat="View view/browserViews">
+              <metal:block use-macro="context/@@interface_macros/View" />
+            </li>
+          </ul>
+        </div>
+      </tal:block>
   
-  <div class="indent" 
-       tal:define="adapters view/getRequiredAdapters"
-       tal:condition="adapters">
+      <tal:block condition="view/xmlrpcViews">
+        <h3 class="details-section" i18n:translate="">XML-RPC</h3>
+        <div class="indent">    
+          <ul class="attr-list">
+            <li tal:repeat="View view/xmlrpcViews">
+              <metal:block use-macro="context/@@interface_macros/View" />
+            </li>
+          </ul>
+        </div>
+      </tal:block>
+  
+      <tal:block condition="view/httpViews">
+        <h3 class="details-section" i18n:translate="">HTTP</h3>
+        <div class="indent">    
+          <ul class="attr-list">
+            <li tal:repeat="View view/httpViews">
+              <metal:block use-macro="context/@@interface_macros/View" />
+            </li>
+          </ul>
+        </div>
+      </tal:block>
+  
+      <tal:block condition="view/ftpViews">
+        <h3 class="details-section" i18n:translate="">FTP</h3>
+        <div class="indent">    
+          <ul class="attr-list">
+            <li tal:repeat="View view/ftpViews">
+              <metal:block use-macro="context/@@interface_macros/View" />
+            </li>
+          </ul>
+        </div>
+      </tal:block>
+  
+      <tal:block condition="view/otherViews">
+        <h3 class="details-section" i18n:translate="">Other</h3>
+        <div class="indent">    
+          <ul class="attr-list">
+            <li tal:repeat="View view/otherViews">
+              <metal:block use-macro="context/@@interface_macros/View" />
+            </li>
+          </ul>
+        </div>
+      </tal:block>
 
-    <h3 i18n:translate="">Adapters where this interface is required:</h3>
-    <ul class="attr-list">
-      <li tal:repeat="adapter adapters">
-        <b><code>
-          <a href=""
-                tal:attributes="href 
-                    string:../../Class/${adapter/factory_url}/index.html" 
-                tal:content="adapter/factory" 
-                tal:condition="adapter/factory_url" />
-          <span tal:content="adapter/factory" 
-                tal:condition="not: adapter/factory_url" />
-        </code></b>
-        <span tal:condition="adapter/name">
-          (<span i18n:translate="">name:</span>
-            <tal:block content="adapter/name" />)
-        </span>
-        <br />
-        <tal:block condition="adapter/provided">
-        <i i18n:translate="">provides:</i> 
-          <a href=""
-              tal:attributes="href string:../${adapter/provided}/apiindex.html"
-              tal:content="adapter/provided" /><br />
-        </tal:block>
-        <tal:block condition="adapter/required">
-        <i i18n:translate="">also required:</i>
-          <tal:block repeat="iface adapter/required">
-            <a href=""
-                tal:attributes="href string:../$iface/apiindex.html"
-                tal:content="iface" /><tal:block 
-                condition="not:repeat/iface/end">, </tal:block>
-          </tal:block>
-        </tal:block>
-      </li>
-    </ul>
-
+    </div>
   </div>
 
-  <div class="indent" 
-       tal:define="adapters view/getProvidedAdapters"
-       tal:condition="adapters">
-
-    <h3 i18n:translate="">Adapters that provide this interface:</h3>
-    <ul>
-      <li tal:repeat="adapter adapters">
-        <b><code>
-          <a href=""
-                tal:attributes="href
-                    string:../../Class/${adapter/factory_url}/index.html" 
-                tal:content="adapter/factory" 
-                tal:condition="adapter/factory_url" />
-          <span tal:content="adapter/factory" 
-                tal:condition="not: adapter/factory_url" />
-        </code></b>
-        <span tal:condition="adapter/name">
-          (<span i18n:translate="">name:</span>
-            <tal:block content="adapter/name" />)
-        </span>
-        <br />
-        <i i18n:translate="">requires:</i> 
-          <tal:block repeat="iface adapter/required">
-            <a href=""
-                tal:condition="iface"
-                tal:attributes="href string:../$iface/"
-                tal:content="iface" /><tal:block 
-                condition="not:repeat/iface/end">, </tal:block>
-            <span tal:condition="not: iface" i18n:translate="">
-              No interface required.
-            </span>
-          </tal:block>
-      </li>
-    </ul>
-
-  </div>
-
-  <p tal:condition="
-     python: not (view.getRequiredAdapters() or view.getProvidedAdapters())">
-    <em i18n:translate="">There are no adapters registered for
-        this interface.</em>
-  </p>
-
   <div tal:define="factories view/getFactories;
-                   utilities view/getUtilitiesFor;
+                   utilities view/getUtilities;
                    classes view/getClasses"
        tal:condition="python: factories or utilities or classes">
 
@@ -212,11 +205,7 @@
           </div>
           <ul>
             <li tal:repeat="factory factories">
-              <a href=""
-                 tal:attributes="href string:../../Class/${factory/url}/"
-                 tal:content="factory/name" />
-              <tal:block replace="string:(${factory/title})" 
-                         condition="factory/title" />
+              <metal:block use-macro="context/@@interface_macros/factory" />
             </li>
           </ul>
         </div>
@@ -231,17 +220,7 @@
           </div>
           <ul>
             <li tal:repeat="utility utilities">
-              <a href=""
-                 tal:attributes="href 
-              string:../../Utility/${view/getId}/${utility/url_name}/index.html"
-                 tal:content="structure utility/name" />
-              <br />
-              <div><span i18n:translate="">Class:</span> <code>
-                <a href=""
-                   tal:attributes="href 
-                       string:../../Class/${utility/url}/index.html"
-                   tal:content="utility/path" />
-              </code></div>              
+              <metal:block use-macro="context/@@interface_macros/utility" />
             </li>
           </ul>
         </div>
@@ -256,12 +235,7 @@
           </div>
           <ul>
             <li tal:repeat="klass classes">
-              <b><code>
-                <a href=""
-                   tal:attributes="href 
-                       string:../../Class/${klass/url}/index.html"
-                   tal:content="klass/path" />
-              </code></b>
+              <metal:block use-macro="context/@@interface_macros/class" />
             </li>
           </ul>
         </div>
@@ -270,10 +244,5 @@
     </div>
   </div>
 
-  <p>
-    <em><b class="required">*</b> =
-    <span i18n:translate="">required</span></em>
-  </p>
-
 </body>
 </html>

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/macros.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/macros.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/macros.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,22 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+"""API Documentation macros
+
+$Id: macros.py 25177 2004-06-02 13:17:31Z jim $
+"""
+from zope.app.basicskin.standardmacros import StandardMacros
+
+class InterfaceDetailsMacros(StandardMacros):
+    """Page Template METAL macros for Interfaces"""
+    macro_pages = ('iface_macros', 'component_macros', 'presentation_macros')

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -13,50 +13,13 @@
 ##############################################################################
 """Interface Module Browser Menu (Tree)
 
-A list of interfaces from the global site manager is pretty much unmanagable and
-is very confusing. Therefore it is nice to split the path of the interface, so
-that we get a deeper tree with nodes having shorter, manageable names.
-
 $Id$
 """
 __docformat__ = 'restructuredtext'
 from zope.security.proxy import removeSecurityProxy
 
 def getAllTextOfInterface(iface):
-    """Get all searchable text from an interface
-
-    Example:
-
-    >>> import zope.interface
-    >>> import zope.schema
-    >>> class IFoo(zope.interface.Interface):
-    ...     '''foo'''
-    ...
-    ...     def bar(self):
-    ...         '''bar'''
-    ...
-    ...     blah = zope.interface.Attribute('blah', 'blah')
-    ...
-    ...     field = zope.schema.Field(
-    ...         title = u'title', description = u'description')
-    
-    Now get the text. Note that there is no particular order during the text
-    collection.
-    
-    >>> text = getAllTextOfInterface(IFoo)
-    >>> u'foo' in text
-    True
-    >>> u'bar' in text
-    True
-    >>> u'blah' in text
-    True
-    >>> u'field' in text
-    True
-    >>> u'title' in text
-    True
-    >>> u'description' in text
-    True
-    """
+    """Get all searchable text from an interface"""
     iface = removeSecurityProxy(iface)
     text = iface.__doc__ or ''
     for name in iface:
@@ -67,49 +30,11 @@
 
 
 class Menu(object):
-    """Menu for the Interface Documentation Module.
+    """Menu for the Interface Documentation Module."""
 
-    The menu allows for looking for interfaces by full-text search or partial
-    names. See `findInterfaces()` for the simple search implementation.
-    """
-
     def findInterfaces(self):
         """Find the interface that match any text in the documentation strings
-        or a partial path.
-
-        Before we can test the method, let's create a Menu instance:
-
-        >>> from zope.interface.interfaces import IElement, IAttribute
-
-        >>> menu = Menu()
-        >>> menu.context = {'IElement': IElement, 'IAttribute': IAttribute}
-        >>> menu.request = {'name_only': 'on', 'search_str': ''}
-
-        Now let's see how successful our searches are:
-
-        >>> from zope.app.apidoc.tests import pprint
-        >>> menu.request['search_str'] = 'Elem'
-        >>> pprint(menu.findInterfaces())
-        [[('name', 'IElement'), ('url', './IElement/apiindex.html')]]
-
-        >>> menu.request['search_str'] = 'I'
-        >>> pprint(menu.findInterfaces())
-        [[('name', 'IAttribute'), ('url', './IAttribute/apiindex.html')],
-         [('name', 'IElement'), ('url', './IElement/apiindex.html')]]
-
-        Now using the full text search:
-
-        >>> del menu.request['name_only']
-
-        >>> menu.request['search_str'] = 'object'
-        >>> pprint(menu.findInterfaces())
-        [[('name', 'IAttribute'), ('url', './IAttribute/apiindex.html')],
-         [('name', 'IElement'), ('url', './IElement/apiindex.html')]]
-
-        >>> menu.request['search_str'] = 'Stores'
-        >>> pprint(menu.findInterfaces())
-        [[('name', 'IAttribute'), ('url', './IAttribute/apiindex.html')]]
-        """
+        or a partial path."""
         name_only = ('name_only' in self.request) and True or False
         search_str = self.request.get('search_str', None)
         results = []

Added: Zope3/trunk/src/zope/app/apidoc/ifacemodule/presentation_macros.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/presentation_macros.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/presentation_macros.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,91 @@
+<metal:block define-macro="View">
+  <div>
+    <b tal:content="structure View/name" />
+  </div>
+
+  <span class="small">
+
+    <div tal:define="zcml View/zcml"
+         tal:condition="zcml">
+      <i i18n:translate="">registered:</i> 
+      <metal:block use-macro="context/@@interface_macros/zcml" />
+    </div>
+
+    <div tal:define="iface View/type"
+         tal:condition="python: iface == View['required'][-1]">
+      <i i18n:translate="">presentation type:</i>
+      <a href=""
+         tal:attributes="href
+                string:../${iface/module}.${iface/name}/apiindex.html">
+          <metal:block use-macro="context/@@interface_macros/ifacename" />
+      </a>
+    </div>
+
+    <div tal:define="ifaces python:View['required'][:-1]">
+      <i i18n:translate="">requires:</i>
+      <tal:block repeat="iface ifaces">
+        <a href=""
+            tal:condition="iface"
+            tal:attributes="href 
+                string:../${iface/module}.${iface/name}/apiindex.html">
+          <metal:block use-macro="context/@@interface_macros/ifacename" 
+        /></a><tal:block condition="not:repeat/iface/end">, </tal:block>
+      </tal:block>
+      <span tal:condition="not: ifaces" i18n:translate="">
+        No interface required.
+      </span>
+    </div>
+
+    <div tal:define="iface View/provided"
+         tal:condition="iface">
+      <i i18n:translate="">provides:</i>
+      <a href=""
+         tal:attributes="href 
+             string:../${iface/module}.${iface/name}/apiindex.html">
+        <metal:block use-macro="context/@@interface_macros/ifacename" />
+      </a>
+    </div>
+
+    <div tal:define="iface View/layer"
+         tal:condition="iface">
+      <i i18n:translate="">layer:</i>
+      <a href=""
+         tal:attributes="href 
+             string:../${iface/module}.${iface/name}/apiindex.html">
+        <metal:block use-macro="context/@@interface_macros/ifacename" />
+      </a>
+    </div>
+
+    <div condition="View/factory/path">
+      <i i18n:translate="">factory path:</i>
+      <a href=""
+         tal:condition="View/factory/referencable"
+         tal:attributes="href 
+             string: ../../Code/${View/factory/url}/index.html"
+         tal:content="View/factory/path" />
+      <span
+         tal:condition="not:View/factory/referencable"
+         tal:content="View/factory/path" />
+    </div>
+
+    <div tal:condition="View/factory/template">
+      <i i18n:translate="">template:</i>
+      <span tal:replace="View/factory/template" />
+    </div>
+
+    <div tal:condition="View/factory/resource">
+      <i i18n:translate="">resource:</i>
+      <a href=""
+         tal:attributes="href 
+             string: /@@/${View/factory/resource}"
+         tal:content="View/factory/resource" />
+    </div>
+
+    <div tal:condition="View/read_perm">
+      <i i18n:translate="">Permission:</i>
+      <span tal:replace="View/read_perm">zope.View</span>
+    </div>
+
+  </span>
+
+</metal:block>

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/tests.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/tests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -18,113 +18,42 @@
 import unittest
 
 from zope.component.interfaces import IFactory
-from zope.component.factory import Factory
-from zope.interface import implements, Interface, Attribute
 from zope.interface.interfaces import IInterface
-from zope.publisher.browser import TestRequest
-from zope.schema import TextLine, Text
-from zope.testing.doctestunit import DocTestSuite
+from zope.testing import doctest, doctestunit
 
-from zope.app.component.interface import provideInterface
-from zope.app.location import LocationProxy
-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.interfaces import IContainmentRoot
-from zope.app.traversing.interfaces import IPhysicallyLocatable
+from zope.app.testing import placelesssetup, ztapi, setup
 from zope.app.tree.interfaces import IUniqueId
 from zope.app.tree.adapters import LocationUniqueId 
 
-from zope.app.apidoc.classmodule import classRegistry
-from zope.app.apidoc.ifacemodule import IInterfaceModule, InterfaceModule
-from zope.app.apidoc.ifacemodule.browser import InterfaceDetails
-from zope.app.apidoc.interfaces import IDocumentationModule
-
-
-class Root(object):
-    implements(IContainmentRoot)
-
-    __parent__ = None
-    __name__ = ''
-
-def rootLocation(obj, name):
-    return LocationProxy(obj, Root(), name)
-
-class IFoo(Interface):
-    """This is the Foo interface
-
-    More description here...
-    """
-    foo = Attribute('This is foo.')
-    bar = Attribute('This is bar.')
-
-    title = TextLine(description=u'Title', required=True, default=u'Foo')
-    description = Text(description=u'Desc', required=False, default=u'Foo.')
-
-    def blah():
-        """This is blah."""
-    
-    def get(key, default=None):
-        """This is get."""
-
-class IBar(Interface):
-    pass
-
-class Foo(object):
-    implements(IFoo)
-
-
-def getInterfaceDetails():
-    ifacemodule = InterfaceModule()
-    ifacemodule.__parent__ = Root()
-    ifacemodule.__name__ = 'Interfaces'
-    iface = LocationProxy(IFoo, ifacemodule, 'IFoo')
-    view = InterfaceDetails()
-    view.context = iface
-    view.request = TestRequest()
-    return view
-    
-
 def setUp(test):
     placelesssetup.setUp()
-    provideInterface(None, IDocumentationModule)
-    provideInterface('IInterfaceModule', IInterfaceModule)
+    setup.setUpTraversal()
+    
     ztapi.provideAdapter(IInterface, IUniqueId, LocationUniqueId)
-    ztapi.provideAdapter(None, IPhysicallyLocatable,
-                         LocationPhysicallyLocatable)
 
     # 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)
+    # Cheat and register the ReST factory for STX as well
+    ztapi.provideUtility(IFactory, ReStructuredTextSourceFactory,
+                         'zope.source.stx')    
 
-    # Make IFoo adapter interesting.
-
-    ztapi.provideAdapter(IBar, IFoo, object)
-    classRegistry[Foo.__module__ + '.' + Foo.__name__] = Foo
-    ztapi.provideUtility(IFactory, Factory(Foo, title='Foo Factory'),
-                         'FooFactory')
-    ztapi.provideUtility(IFoo, Foo(), 'The Foo')
-
     
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.app.apidoc.ifacemodule',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
-        DocTestSuite('zope.app.apidoc.ifacemodule.menu',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
-        DocTestSuite('zope.app.apidoc.ifacemodule.browser',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp, tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('browser.txt',
+                             setUp=setUp, tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
         ))
 
 if __name__ == '__main__':

Added: Zope3/trunk/src/zope/app/apidoc/interface.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/interface.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/interface.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,135 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Interface Inspection Utilities
+
+$Id: browser.py 29199 2005-02-17 22:38:55Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.interface import Interface, providedBy
+from zope.interface.interfaces import IInterface, ISpecification
+from zope.interface.interfaces import IElement, IAttribute, IMethod
+from zope.schema.interfaces import IField
+
+from zope.app.apidoc.utilities import getPythonPath, renderText
+
+
+def getElements(iface, type=IElement):
+    """Return a dictionary containing the elements in an interface.
+
+    The type specifies whether we are looking for attributes or methods."""
+    items = {}
+    for name in iface:
+        attr = iface[name]
+        if type.providedBy(attr):
+            items[name] = attr
+    return items
+
+
+def getFieldsInOrder(iface,
+                     _itemsorter=lambda x, y: cmp(x[1].order, y[1].order)):
+    """Return a list of (name, field) tuples in native interface order."""
+    items = getElements(iface, IField).items()
+    items.sort(_itemsorter)
+    return items
+
+
+def getAttributes(iface):
+    """Returns a list of attributes specified in the interface."""
+    return [(name, attr)
+            for name, attr in getElements(iface, IAttribute).items()
+            if not (IField.providedBy(attr) or IMethod.providedBy(attr))]
+
+
+def getMethods(iface):
+    """Returns a list of methods specified in the interface."""
+    return getElements(iface, IMethod).items()
+
+
+def getFields(iface):
+    """Returns a list of fields specified in the interface."""
+    return getFieldsInOrder(iface)
+
+
+def getInterfaceTypes(iface):
+    """Return a list of interface types that are specified for this
+    interface.
+
+    Note that you should only expect one type at a time.
+    """
+    types = list(providedBy(iface).flattened())
+    # Remove interfaces provided by every interface instance
+    types.remove(ISpecification)
+    types.remove(IElement)
+    types.remove(Interface)    
+    # Remove interface provided by every interface type
+    types.remove(IInterface)
+    return types
+
+
+def getFieldInterface(field):
+    """Return the interface representing the field."""
+    name = field.__class__.__name__
+    field_iface = None
+    ifaces = tuple(providedBy(field).flattened())
+    for iface in ifaces:
+        # All field interfaces implement `IField`. In case the name match
+        # below does not work, use the first `IField`-based interface found
+        if field_iface is None and iface.extends(IField):
+            field_iface = iface
+
+        # Usually fields have interfaces with the same name (with an 'I')
+        if iface.getName() == 'I' + name:
+            return iface
+
+    # If not even a `IField`-based interface was found, return the first
+    # interface of the implemented interfaces list.  
+    return field_iface or ifaces[0]
+    
+
+def getAttributeInfoDictionary(attr, format='zope.source.rest'):
+    """Return a page-template-friendly information dictionary."""
+    return {'name': attr.getName(),
+            'doc': renderText(attr.getDoc() or u'', format=format)}
+
+
+def getMethodInfoDictionary(method, format='zope.source.rest'):
+    """Return a page-template-friendly information dictionary."""
+    return {'name': method.getName(),
+            'signature': method.getSignatureString(),
+            'doc': renderText(method.getDoc() or u'', format=format)}
+
+
+def getFieldInfoDictionary(field, format='zope.source.rest'):
+    """Return a page-template-friendly information dictionary."""
+
+    info = {'name': field.getName(),
+            'required': field.required,
+            'required_string': field.required and u'required' or u'optional',
+            'default': repr(field.default),
+            'title': field.title}
+
+    # Determine the interface of the field
+    iface = getFieldInterface(field)
+    info['iface'] = {'name': iface.getName(), 'id': getPythonPath(iface)}
+
+    # Determine the field class
+    class_ = field.__class__
+    info['class'] = {'name': class_.__name__,
+                     'path': getPythonPath(class_).replace('.', '/')}
+
+    # Render the field description
+    info['description'] = renderText(field.description or u'', format=format)
+                                     
+    return info

Added: Zope3/trunk/src/zope/app/apidoc/interface.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/interface.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/interface.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,249 @@
+==============================
+Interface Inspection Utilities
+==============================
+
+This document is a presentation of the utility functions provided by
+
+  >>> from zope.app.apidoc import interface
+
+For the following demonstrations, we need a nice interface that we can inspect:
+
+  >>> from zope.interface import Interface, Attribute
+  >>> from zope.schema import Field, TextLine
+
+  >>> class IFoo(Interface):
+  ...     foo = Field(title=u"Foo")
+  ...
+  ...     bar = TextLine(title=u"Bar",
+  ...                    description=u"The Bar",
+  ...                    required=True,
+  ...                    default=u"My Bar")
+  ...
+  ...     baz = Attribute('baz', 
+  ...                     'This is the baz attribute')
+  ...
+  ...     def blah(one, two, three=None, *args, **kwargs):
+  ...         """This is the `blah` method."""
+
+
+`getElements(iface, type=IElement)`
+-----------------------------------
+
+Return a dictionary containing all elements in an interface. The type
+specifies whether we are looking for attributes, fields or methods. So let's
+look at an example.
+
+First, let's get the methods of an interface:
+
+  >>> from zope.interface.interfaces import IMethod
+  >>> interface.getElements(IFoo, type=IMethod).keys()
+  ['blah']
+
+and now the fields:
+
+  >>> from zope.schema.interfaces import IField
+  >>> names = interface.getElements(IFoo, type=IField).keys()
+  >>> names.sort()
+  >>> names
+  ['bar', 'foo']
+
+We can also get all attributes of course.
+
+  >>> from zope.interface.interfaces import IAttribute
+  >>> names = interface.getElements(IFoo, type=IAttribute).keys()
+  >>> names.sort()
+  >>> names
+  ['bar', 'baz', 'blah', 'foo']
+
+You might be surprised by the above result, since the fields and methods are
+again included. However, fields and methods are just attributes and thus
+extend the simple attribute implementation. If you want to get a list of
+attributes that does not include fields and methods, see the
+`getAttributes(iface)` function.
+
+The default type is `IElement` which will simply return all elements of the
+interface:
+
+  >>> names = interface.getElements(IFoo).keys()
+  >>> names.sort()
+  >>> names
+  ['bar', 'baz', 'blah', 'foo']
+
+Note: The interface you pass to this function *cannot* be proxied!
+Presentation code often like to wrap interfaces in security proxies and apidoc
+even uses location proxies for interface.
+
+
+`getFieldsInOrder(iface, _itemsorter=...)`
+-----------------------------------------------------------
+
+For presentation purposes we often want fields to have the a certain order,
+most comonly the order they have in the interface. This function returns a
+list of (name, field) tuples in a specified order.
+
+The `_itemsorter` argument provides the function that is used to order the
+fields. The default function, which sorts by the fields' `order` attribute,
+should be the correct one for 99% of your needs.
+
+Reusing the interface created above, we check the output:
+
+  >>> [n for n, a in interface.getFieldsInOrder(IFoo)]
+  ['foo', 'bar']
+
+By changing the sort method to sort by names, we get:
+
+  >>> [n for n, a in interface.getFieldsInOrder(
+  ...       IFoo, _itemsorter=lambda x, y: cmp(x[0], y[0]))]
+  ['bar', 'foo']
+
+
+`getAttributes(iface)`
+----------------------
+
+This function returns a (name, attr) tuple for every attribute in the
+interface. Note that this function will only return pure attributes; it
+ignores methods and fields.
+
+  >>> attrs = interface.getAttributes(IFoo)
+  >>> attrs.sort()
+  >>> attrs #doctest: +ELLIPSIS
+  [('baz', <zope.interface.interface.Attribute object at ...>)]
+
+
+`getMethods(iface)`
+-------------------
+
+This function returns a (name, method) tuple for every declared method in the
+interface.
+
+  >>> methods = interface.getMethods(IFoo)
+  >>> methods.sort()
+  >>> methods #doctest: +ELLIPSIS
+  [('blah', <zope.interface.interface.Method object at ...>)]
+
+
+`getFields(iface)`
+------------------
+
+This function returns a (name, field) tuple for every declared field in the
+interface.
+
+  >>> interface.getFields(IFoo) #doctest: +ELLIPSIS
+  [('foo', <zope.schema._bootstrapfields.Field object at ...>), 
+   ('bar', <zope.schema._bootstrapfields.TextLine object at ...>)]
+
+Note that this returns the same result as `getFieldsInOrder()` with the fields
+sorted by their `order` attribute, except that you cannot specify the sort
+function here. This function was mainly provided for symmetry with the other
+functions.
+
+
+`getInterfaceTypes(iface)`
+--------------------------
+
+Interfaces can be categorized/grouped by using interface types. Interface
+types simply extend `zope.interface.interfaces.IInterface`, which are
+basically meta-interfaces. The interface types are then provided by particular
+interfaces.
+
+The `getInterfaceTypes()` function returns a list of interface types that are
+provided for the specified interface. Note that you commonly expect only one
+type per interface, though.
+
+Before we assign any type to our `IFoo` interface, there are no types
+declared.
+
+  >>> interface.getInterfaceTypes(IFoo)
+  []
+
+Now we define a new type called `IContentType`
+
+  >>> from zope.interface.interfaces import IInterface
+  >>> class IContentType(IInterface):
+  ...     pass
+
+and have our interface provide it:
+
+  >>> from zope.interface import directlyProvides
+  >>> directlyProvides(IFoo, IContentType)
+
+Note that ZCML has some more convenient methods of doing this. Now let's get
+the interface types again:
+
+  >>> interface.getInterfaceTypes(IFoo)
+  [<InterfaceClass __builtin__.IContentType>]
+
+Again note that the interface passed to this function *cannot* be proxied,
+otherwise this method will pick up the proxy's interfaces as well.
+
+
+`getFieldInterface(field)`
+--------------------------
+
+This function tries pretty hard to determine the best-matching interface that
+represents the field. Commonly the field class has the same name as the field
+interface (minus an "I"). So this is our first choice:
+
+  >>> from zope.schema import Text, Int
+  >>> interface.getFieldInterface(Text())
+  <InterfaceClass zope.schema.interfaces.IText>
+
+  >>> interface.getFieldInterface(Int())
+  <InterfaceClass zope.schema.interfaces.IInt>
+
+If the name matching method fails, it picks the first interface that extends
+`IField`:
+
+  >>> from zope.schema.interfaces import IField
+  >>> class ISpecialField(IField):
+  ...     pass 
+  >>> class ISomething(Interface):
+  ...     pass
+
+  >>> from zope.interface import implements
+  >>> class MyField:
+  ...     implements(ISomething, ISpecialField)
+
+  >>> interface.getFieldInterface(MyField())
+  <InterfaceClass __builtin__.ISpecialField>
+
+
+`getAttributeInfoDictionary(attr, format='restructuredtext')`
+-------------------------------------------------------------
+
+This function returns a page-template-friendly dictionary for a simple
+attribute:
+
+  >>> pprint(interface.getAttributeInfoDictionary(IFoo['baz']))
+  {'doc': u'<div class="document">\nThis is the baz attribute</div>\n',
+   'name': 'baz'}
+
+
+`getMethodInfoDictionary(method, format='restructuredtext')`
+-------------------------------------------------------------
+
+This function returns a page-template-friendly dictionary for a method:
+
+  >>> pprint(interface.getMethodInfoDictionary(IFoo['blah'])) #doc
+  {'doc': 
+     u'<div class="document">\nThis is the <cite>blah</cite> method.</div>\n',
+   'name': 'blah',
+   'signature': '(one, two, three=None, *args, **kwargs)'}
+
+
+`getFieldInfoDictionary(field, format='restructuredtext')`
+----------------------------------------------------------
+
+This function returns a page-template-friendly dictionary for a field:
+
+  >>> pprint(interface.getFieldInfoDictionary(IFoo['bar']))
+  {'class': {'name': 'TextLine',
+             'path': 'zope/schema/_bootstrapfields/TextLine'},
+   'default': "u'My Bar'",
+   'description': u'<div class="document">\nThe Bar</div>\n',
+   'iface': {'id': 'zope.schema.interfaces.ITextLine',
+             'name': 'ITextLine'},
+   'name': 'bar',
+   'required': True,
+   'required_string': u'required',
+   'title': u'Bar'}
\ No newline at end of file

Added: Zope3/trunk/src/zope/app/apidoc/presentation.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/presentation.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/presentation.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,160 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Views/Presentation Utilities
+
+$Id$
+"""
+from types import ClassType, FunctionType
+from zope.component.site import AdapterRegistration
+from zope.interface import Interface
+
+from zope.app import zapi
+from zope.app.apidoc.utilities import getPythonPath, relativizePath
+from zope.app.apidoc.utilities import getPermissionIds
+from zope.app.apidoc.component import getParserInfoInfoDictionary
+from zope.app.apidoc.component import getInterfaceInfoDictionary
+from zope.app.publisher.browser.icon import IconViewFactory
+
+from zope.publisher.interfaces import IRequest, ILayer
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
+from zope.publisher.interfaces.http import IHTTPRequest
+from zope.publisher.interfaces.ftp import IFTPRequest
+
+SPECIFIC_INTERFACE_LEVEL = 1
+EXTENDED_INTERFACE_LEVEL = 2
+GENERIC_INTERFACE_LEVEL = 4
+
+def getViewFactoryData(factory):
+    """Squeeze some useful information out of the view factory"""
+    info = {'path': None, 'url': None, 'template': None, 'resource': None,
+            'referencable': True}
+
+    if hasattr(factory, '__name__') and \
+       factory.__name__.startswith('SimpleViewClass'):
+        # In the case of a SimpleView, the base is really what we are
+        # interested in. Usually the first listed class is the interesting one.
+        base = factory.__bases__[0]
+        info['path'] = base.__module__ + '.' + base.__name__
+        info['template'] = relativizePath(factory.index.filename)
+
+    elif isinstance(factory, (str, unicode, float, int, list, tuple)):
+        info['referencable'] = False
+
+    elif factory.__module__.startswith('zope.app.publisher.browser.viewmeta'):
+        info['path'] = getPythonPath(factory.__bases__[0])
+
+    elif hasattr(factory, '__class__') and \
+             factory.__class__.__name__ == 'ProxyView':
+        factory = factory.factory
+        info['path'] = factory.__module__ + '.' + factory.__name__
+
+    elif not hasattr(factory, '__name__'):
+        info['path'] = getPythonPath(factory.__class__)
+
+    elif type(factory) in (type, ClassType):
+        info['path'] = getPythonPath(factory)
+
+    elif isinstance(factory, FunctionType):
+        info['path'] = getPythonPath(factory.factory)
+    else:
+        info['path'] = getPythonPath(factory)
+
+    if info['referencable']:
+        info['url'] = info['path'].replace('.', '/')
+
+    if isinstance(factory, IconViewFactory):
+        info['resource'] = factory.rname
+
+    return info
+
+
+def getPresentationType(iface):
+    """Get the presentation type from a layer interface."""
+    # Note that the order of the requests matters here, since we want to
+    # inspect the most specific one first. For example, IBrowserRequest is also
+    # an IHTTPRequest. 
+    for type in [IBrowserRequest, IXMLRPCRequest, IHTTPRequest, IFTPRequest]:
+        if iface.isOrExtends(type):
+            return type
+    return iface
+
+
+def getViews(iface, type=IRequest):
+    """Get all view registrations for a particular interface."""
+    gsm = zapi.getGlobalSiteManager()
+    for reg in gsm.registrations():
+        if (isinstance(reg, AdapterRegistration) and
+            reg.required[-1] is not None and
+            reg.required[-1].isOrExtends(type)):
+
+            for required_iface in reg.required[:-1]:
+                if iface.isOrExtends(required_iface):
+                    yield reg
+
+
+def filterViewRegistrations(regs, iface, level=SPECIFIC_INTERFACE_LEVEL):
+    """Return only those registrations that match the specifed level"""
+    for reg in regs:
+        if level & GENERIC_INTERFACE_LEVEL:
+            for required_iface in reg.required[:-1]:
+                if required_iface in (Interface, None):
+                    yield reg
+                    continue
+
+        if level & EXTENDED_INTERFACE_LEVEL:
+            for required_iface in reg.required[:-1]:
+                if required_iface is not Interface and \
+                       iface.extends(required_iface):
+                    yield reg
+                    continue
+            
+        if level & SPECIFIC_INTERFACE_LEVEL:
+            for required_iface in reg.required[:-1]:
+                if required_iface is iface:
+                    yield reg
+                    continue
+
+
+def getViewInfoDictionary(reg):
+    """Build up an information dictionary for a view registration."""
+    # get configuration info
+    if isinstance(reg.doc, (str, unicode)):
+        doc = reg.doc
+        zcml = None
+    else:
+        doc = None
+        zcml = getParserInfoInfoDictionary(reg.doc)
+
+    # get layer
+    layer = None
+    if ILayer.providedBy(reg.required[-1]):
+        layer = getInterfaceInfoDictionary(reg.required[-1])
+    
+
+    info = {'name' : reg.name or '<i>no name</i>',
+            'type' : getPythonPath(getPresentationType(reg.required[-1])),
+            'factory' : getViewFactoryData(reg.value),
+            'required': [getInterfaceInfoDictionary(iface)
+                         for iface in reg.required],
+            'provided' : getInterfaceInfoDictionary(reg.provided),
+            'layer': layer,
+            'doc': doc,
+            'zcml': zcml
+            }
+    
+    # Educated guess of the attribute name
+    info.update(getPermissionIds('publishTraverse', klass=reg.value))
+        
+    return info

Added: Zope3/trunk/src/zope/app/apidoc/presentation.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/presentation.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/presentation.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,293 @@
+=================================
+Presentation Inspection Utilities
+=================================
+
+The `presentation` module provides some nice utilities to inspect presentation
+registrations.
+
+  >>> from zope.app.apidoc import presentation
+
+
+`getViewFactoryData(factory)`
+-----------------------------
+
+This function tries really hard to determine the correct information about a
+view factories. For example, when you create a page, a new type is dynamically
+generated upon generation. Let's look at a couple examples.
+
+First, let's look at a case where a simple browser page was configured without
+a special view class. In these cases the factory is a `SimpleViewClass`:
+
+  >>> from zope.app.pagetemplate.simpleviewclass import SimpleViewClass
+  >>> view = SimpleViewClass('browser/index.pt')
+  >>> info = presentation.getViewFactoryData(view)
+
+Before we can check the result, we have to make sure that all Windows paths
+are converted to Unix-like paths. We also clip off instance-specific parts of
+the template path:
+
+  >>> info['template'] = info['template'].replace('\\\\', '/')[-32:]
+  >>> pprint(info)
+  {'path': 'zope.app.pagetemplate.simpleviewclass.simple',
+   'referencable': True,
+   'resource': None,
+   'template': 'zope/app/apidoc/browser/index.pt',
+   'url': 'zope/app/pagetemplate/simpleviewclass/simple'}
+
+So in the result above we see what the function returns. It is a dictionary
+(converted to a list for test purposes) that contains the Python path of the
+view class, a flag that specifies whether the factory can be referenced and
+thus be viewed by the class browser, the (page) template used for the view and
+the URL under which the factory will be found in the class browser. Some
+views, like icons, also use resources to provide their data. In these cases
+the name of the resource will be provided. Of course, not in all cases all
+values will be available. Empty values are marked with `None`. 
+
+Believe it or not, in some cases the factory is just a simple type. In these
+cases we cannot retrieve any useful information:
+
+  >>> info = presentation.getViewFactoryData(3)
+  >>> pprint(info)
+  {'path': None,
+   'referencable': False,
+   'resource': None,
+   'template': None,
+   'url': None}
+
+In some cases factories are callable class instances, where we cannot directly
+have a referencable name, so we lookup the class and use its name:
+
+  >>> class Factory(object):
+  ...     pass
+  
+  >>> info = presentation.getViewFactoryData(Factory())
+  >>> pprint(info)
+  {'path': '__builtin__.Factory',
+   'referencable': True,
+   'resource': None,
+   'template': None,
+   'url': '__builtin__/Factory'}
+
+One of the more common cases, however, is that the factory is a class or
+type. In this case we can just retrieve the reference directly: 
+
+  >>> info = presentation.getViewFactoryData(Factory)
+  >>> pprint(info)
+  {'path': '__builtin__.Factory',
+   'referencable': True,
+   'resource': None,
+   'template': None,
+   'url': '__builtin__/Factory'}
+ 
+
+`getPresentationType(iface)`
+----------------------------
+
+In Zope 3, presentation types (i.e. browser, ftp, ...) are defined through
+their special request interface, such as `IBrowserRequest` or
+`IFTPRequest`. To complicate matters further, layer interfaces are used in
+browser presentations to allow skinning. Layers extend any request type, but
+most commonly `IBrowserRequest`. This function inspects the request interface
+of any presentation multi-adapter and determines its type, which is returned
+in form of an interface.
+
+  >>> from zope.app.apidoc.presentation import getPresentationType
+  >>> from zope.publisher.interfaces.http import IHTTPRequest
+  >>> from zope.publisher.interfaces.browser import IBrowserRequest
+
+  >>> class ILayer1(IBrowserRequest): 
+  ...     pass
+
+  >>> presentation.getPresentationType(ILayer1)
+  <InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>
+  
+  >>> class ILayer2(IHTTPRequest): 
+  ...     pass
+
+  >>> presentation.getPresentationType(ILayer2)
+  <InterfaceClass zope.publisher.interfaces.http.IHTTPRequest>
+
+If the function cannot determine the presentation type, the interface itself
+is returned:
+  
+  >>> from zope.interface import Interface
+  >>> class ILayer3(Interface): 
+  ...     pass
+
+  >>> presentation.getPresentationType(ILayer3)
+  <InterfaceClass __builtin__.ILayer3>
+
+Note that more specific presentation types are considered first. For example,
+`IBrowserRequest` extends `IHTTPRequest`, but it will always determine the
+presentation type to be an `IBrowserRequest`.
+
+
+`getViews(iface, type=IRequest)`
+--------------------------------
+
+This function retrieves all available view registrations for a given interface
+and presentation type. The default argument for the presentation type is
+`IRequest`, which will effectively return all views for the specified
+interface.
+
+To see how this works, we first have to register some views:
+
+  >>> class IFoo(Interface):
+  ...     pass
+
+  >>> from zope.app.testing import ztapi
+  >>> ztapi.provideAdapter((IFoo, IHTTPRequest), Interface, None, name='foo')
+  >>> ztapi.provideAdapter((Interface, IHTTPRequest), Interface, None, 
+  ...                      name='bar')
+  >>> ztapi.provideAdapter((IFoo, IBrowserRequest), Interface, None, 
+  ...                      name='blah')
+
+Now let's see what we've got. If we do not specify a type, all registrations
+should be returned:
+
+  >>> regs = list(presentation.getViews(IFoo)) 
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('IFoo', 'IBrowserRequest'), 'Interface', 
+                       'blah', None, ''), 
+   AdapterRegistration(('IFoo', 'IHTTPRequest'), 'Interface', 
+                       'foo', None, ''), 
+   AdapterRegistration(('Interface', 'IHTTPRequest'), 'Interface', 
+                       'bar', None, '')]
+
+  >>> regs = list(presentation.getViews(Interface, IHTTPRequest))
+  >>> regs.sort()
+  >>> regs
+  [AdapterRegistration(('Interface', 'IHTTPRequest'), 'Interface', 
+   'bar', None, '')]
+
+
+`filterViewRegistrations(regs, iface, level=SPECIFC_INTERFACE_LEVEL)`
+---------------------------------------------------------------------
+
+Oftentimes the amount of views that are being returned for a particular
+interface are too much to show at once. It is then good to split the view into
+categories. The `filterViewRegistrations()` function allows you to filter the
+views on how specific they are to the interface. Here are the three levels you
+can select from:
+
+  * SPECIFC_INTERFACE_LEVEL -- Only return registrations that require the
+                               specified interface directly.
+
+  * EXTENDED_INTERFACE_LEVEL -- Only return registrations that require an
+                                interface that the specified interface extends.
+
+  * GENERIC_INTERFACE_LEVEL -- Only return registrations that explicitely 
+                               require the `Interface` interface.
+
+So, let's see how this is done. We first need to create a couple of interfaces
+and register some views:
+
+  >>> class IContent(Interface):
+  ...     pass
+  >>> class IFile(IContent):
+  ...     pass
+
+  Clear out the registries first, so we know what we have.
+  >>> from zope.testing.cleanup import cleanUp
+  >>> cleanUp()
+
+  >>> ztapi.provideAdapter((IContent, IHTTPRequest), Interface, 
+  ...                      None, name='view.html')
+  >>> ztapi.provideAdapter((IContent, IHTTPRequest), Interface, 
+  ...                      None, name='edit.html')
+  >>> ztapi.provideAdapter((IFile, IHTTPRequest), Interface, 
+  ...                      None, name='view.html')
+  >>> ztapi.provideAdapter((Interface, IHTTPRequest), Interface, 
+  ...                      None, name='view.html')
+
+Now we get all the registrations:
+
+  >>> regs = list(presentation.getViews(IFile, IHTTPRequest))
+  
+Let's now filter those registrations:
+
+  >>> result = list(presentation.filterViewRegistrations(
+  ...     regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL))
+  >>> result.sort()
+  >>> result
+  [AdapterRegistration(('IFile', 'IHTTPRequest'), 'Interface', 
+                       'view.html', None, '')]
+
+  >>> result = list(presentation.filterViewRegistrations(
+  ...     regs, IFile, level=presentation.EXTENDED_INTERFACE_LEVEL))
+  >>> result.sort()
+  >>> result
+  [AdapterRegistration(('IContent', 'IHTTPRequest'), 'Interface', 'edit.html', 
+                       None, ''), 
+   AdapterRegistration(('IContent', 'IHTTPRequest'), 'Interface', 'view.html', 
+                       None, '')]
+
+  >>> result = list(presentation.filterViewRegistrations(
+  ...     regs, IFile, level=presentation.GENERIC_INTERFACE_LEVEL))
+  >>> result.sort()
+  >>> result
+  [AdapterRegistration(('Interface', 'IHTTPRequest'), 'Interface', 'view.html', 
+                       None, '')]
+
+You can also specify multiple levels at once using the Boolean OR operator,
+since all three levels are mutually exclusive.
+
+  >>> result = list(presentation.filterViewRegistrations(
+  ...     regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL |
+  ...                        presentation.EXTENDED_INTERFACE_LEVEL))
+  >>> result.sort()
+  >>> result
+  [AdapterRegistration(('IContent', 'IHTTPRequest'), 'Interface', 
+                       'edit.html', None, ''), 
+   AdapterRegistration(('IContent', 'IHTTPRequest'), 'Interface', 
+                       'view.html', None, ''), 
+   AdapterRegistration(('IFile', 'IHTTPRequest'), 'Interface', 
+                       'view.html', None, '')]
+
+  >>> result = list(presentation.filterViewRegistrations(
+  ...     regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL |
+  ...                        presentation.GENERIC_INTERFACE_LEVEL))
+  >>> result.sort()
+  >>> result
+  [AdapterRegistration(('IFile', 'IHTTPRequest'), 'Interface', 
+                       'view.html', None, ''), 
+   AdapterRegistration(('Interface', 'IHTTPRequest'), 'Interface', 
+                       'view.html', None, '')]
+
+
+`getViewInfoDictionary(reg)`
+----------------------------
+
+Now that we have all these utilities to select the registrations, we need to
+prepare the them for output. For page templates the best data structures are
+dictionaries and tuples/lists. This utility will generate an informational
+dictionary for the specified registration.
+
+Let's first create a registration:
+
+  >>> from zope.component.site import AdapterRegistration
+  >>> reg = AdapterRegistration((IFile, Interface, IHTTPRequest), 
+  ...                           Interface, 'view.html', Factory, 'reg info')
+
+  >>> pprint(presentation.getViewInfoDictionary(reg))
+  {'doc': 'reg info',
+   'factory': {'path': '__builtin__.Factory',
+               'referencable': True,
+               'resource': None,
+               'template': None,
+               'url': '__builtin__/Factory'},
+   'layer': None,
+   'name': 'view.html',
+   'provided': {'module': 'zope.interface',
+                'name': 'Interface'},
+   'read_perm': None,
+   'required': [{'module': '__builtin__',
+                 'name': 'IFile'},
+                {'module': 'zope.interface',
+                 'name': 'Interface'},
+                {'module': 'zope.publisher.interfaces.http',
+                 'name': 'IHTTPRequest'}],
+   'type': 'zope.publisher.interfaces.http.IHTTPRequest',
+   'write_perm': None,
+   'zcml': None}

Modified: Zope3/trunk/src/zope/app/apidoc/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/tests.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/tests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -19,14 +19,12 @@
 import unittest
 
 from zope.component.interfaces import IFactory
+from zope.interface import implements
+from zope.testing import doctest, doctestunit
+
 from zope.app.traversing.interfaces import IContainmentRoot
 from zope.app.location import LocationProxy
 from zope.app.testing import placelesssetup, ztapi
-from zope.interface import implements
-from zope.app.apidoc.interfaces import IDocumentationModule
-from zope.app.apidoc.ifacemodule import InterfaceModule
-from zope.app.apidoc.zcmlmodule import ZCMLModule
-from zope.testing.doctestunit import DocTestSuite
 
 from zope.app.renderer.rest import ReStructuredTextSourceFactory
 from zope.app.renderer.rest import IReStructuredTextSource
@@ -35,15 +33,16 @@
 
 def setUp(test):
     placelesssetup.setUp()
-    ztapi.provideUtility(IDocumentationModule, InterfaceModule(),
-                           'Interface')
-    ztapi.provideUtility(IDocumentationModule, ZCMLModule(), 'ZCML')
-
     # Register Renderer Components
     ztapi.provideUtility(IFactory, ReStructuredTextSourceFactory,
                          'zope.source.rest')    
     ztapi.browserView(IReStructuredTextSource, '', 
                       ReStructuredTextToHTMLRenderer)
+    # Cheat and register the ReST renderer as the STX one as well.
+    ztapi.provideUtility(IFactory, ReStructuredTextSourceFactory,
+                         'zope.source.stx')    
+    ztapi.browserView(IReStructuredTextSource, '', 
+                      ReStructuredTextToHTMLRenderer)
 
 
 # Generally useful classes and functions
@@ -57,60 +56,40 @@
 def rootLocation(obj, name):
     return LocationProxy(obj, Root(), name)
 
-
-def _convertToSortedSequence(obj):
-    """Convert data structures containing dictionaries to data structures
-    using sequences only.
-
-    Examples::
-
-      >>> _convertToSortedSequence(())
-      ()
-      >>> _convertToSortedSequence({})
-      []
-      >>> _convertToSortedSequence({'foo': 1})
-      [('foo', 1)]
-      >>> _convertToSortedSequence({'foo': 1, 'bar': 2})
-      [('bar', 2), ('foo', 1)]
-      >>> _convertToSortedSequence({'foo': {1: 'a'}, 'bar': 2})
-      [('bar', 2), ('foo', [(1, 'a')])]
-    """
-
-    # Handle incoming sequences
-    if isinstance(obj, (tuple, list)):
-        objtype = type(obj)
-        result = []
-        for value in obj:
-            result.append(_convertToSortedSequence(value))
-        return objtype(result)
-
-    # Handle Dictionaries
-    if isinstance(obj, dict):
-        result = []
-        for key, value in obj.items():
-            result.append((key, _convertToSortedSequence(value)))
-        result.sort()
-        return result
-
-    return obj
-
-
-def pprint(info):
-    """Print a datastructure in a nice format."""
-    info = _convertToSortedSequence(info)
-    return PrettyPrinter(width=69).pprint(info)
-
-
-    
+     
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.app.apidoc',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
-        DocTestSuite('zope.app.apidoc.browser.apidoc',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
-        DocTestSuite('zope.app.apidoc.utilities'),
-        DocTestSuite('zope.app.apidoc.tests'),
+        doctest.DocTestSuite('zope.app.apidoc.browser.apidoc',
+                             setUp=setUp, tearDown=placelesssetup.tearDown),
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('classregistry.txt',
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('interface.txt',
+                             setUp=setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('component.txt',
+                             setUp=setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('presentation.txt',
+                             setUp=placelesssetup.setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('utilities.txt',
+                             setUp=setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
         ))
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(default="test_suite")

Modified: Zope3/trunk/src/zope/app/apidoc/utilities.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilities.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilities.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -46,46 +46,7 @@
 
 
 class ReadContainerBase(object):
-    """Base for `IReadContainer` objects.
-
-    This is a base class that minimizes the implementation of an
-    `IReadContainer` to two methods, `get()` and `items()`, since the other
-    methods can be implemented using these two.
-
-    Demonstration::
-
-      Make a sample implementation first
-
-      >>> class Container(ReadContainerBase):
-      ...     def get(self, key, default=None):
-      ...         return {'a': 1, 'b': 2}.get(key, default)
-      ...     def items(self):
-      ...         return [('a', 1), ('b', 2)]
-
-      >>> container = Container()
-
-      Now we can use the methods
-
-      >>> container.get('a')
-      1
-      >>> container.get('c') is None
-      True
-      >>> container['b']
-      2
-
-      >>> container.items()
-      [('a', 1), ('b', 2)]
-      >>> container.keys()
-      ['a', 'b']
-      >>> container.values()
-      [1, 2]
-
-      >>> 1 in container
-      True
-      >>> len(container)
-      2
-    """
-
+    """Base for `IReadContainer` objects."""
     implements(IReadContainer)
 
     def get(self, key, default=None):
@@ -102,7 +63,7 @@
         return obj
 
     def __contains__(self, key):
-        return self.get(key) is None
+        return self.get(key) is not None
 
     def keys(self):
         return map(lambda x: x[0], self.items())
@@ -118,40 +79,7 @@
 
 
 def getPythonPath(obj):
-    """Return the path of the object in standard Python notation.
-
-    This method makes only sense for classes and interfaces. Instances do not
-    have a `__name__` attribute, so we would expect them to fail.
-
-    If a method is passed in, its class path is returned, since this is the
-    only path we have a page for.
-
-    Example::
-
-      >>> from zope.interface import Interface
-      >>> class ISample(Interface):
-      ...     pass
-      >>> class Sample(object):
-      ...     def sample(self):
-      ...         pass
-
-      >>> getPythonPath(ISample)
-      'zope.app.apidoc.utilities.ISample'
-
-      >>> getPythonPath(Sample)
-      'zope.app.apidoc.utilities.Sample'
-
-      For methods getPythonPath returns the class path:
-
-      >>> getPythonPath(Sample.sample)
-      'zope.app.apidoc.utilities.Sample'
-
-      >>> try:
-      ...   getPythonPath(Sample())
-      ... except AttributeError:
-      ...   print 'failed'
-      failed
-    """
+    """Return the path of the object in standard Python notation."""
     if obj is None:
         return None
 
@@ -161,8 +89,11 @@
     naked = removeSecurityProxy(obj)
     if hasattr(naked, "im_class"):
         naked = naked.im_class
-    module = naked.__module__
-    return '%s.%s' %(module, naked.__name__)
+    module = getattr(naked, '__module__', _marker)
+    if module is _marker:
+        return naked.__name__
+    else:
+        return '%s.%s' %(module, naked.__name__)
 
 
 def _evalId(id):
@@ -174,69 +105,7 @@
         
 
 def getPermissionIds(name, checker=_marker, klass=_marker):
-    """Get the permissions of an attribute.
-
-    Either the klass or the checker must be specified. If the class is
-    specified, then the checker for it is looked up. Furthermore, this
-    function only works with `INameBasedChecker` checkers. If another checker
-    is found, ``None`` is returned for the permissions.
-
-    Example::
-
-      We first define the class and then the checker for it
-
-      >>> from zope.security.checker import Checker, defineChecker
-      >>> from zope.security.checker import CheckerPublic
-
-      >>> class Sample(object):
-      ...     attr = 'value'
-      ...     attr3 = 'value3'
-
-      >>> class Sample2(object):
-      ...      pass
-
-      >>> checker = Checker({'attr': 'zope.Read', 'attr3': CheckerPublic},
-      ...                   {'attr': 'zope.Write', 'attr3': CheckerPublic}) 
-      >>> defineChecker(Sample, checker)
-
-      Now let's see how this function works
-
-      >>> entries = getPermissionIds('attr', klass=Sample)
-      >>> entries['read_perm']
-      'zope.Read'
-      >>> entries['write_perm']
-      'zope.Write'
-
-      >>> entries = getPermissionIds('attr', getCheckerForInstancesOf(Sample))
-      >>> entries['read_perm']
-      'zope.Read'
-      >>> entries['write_perm']
-      'zope.Write'
-
-      Sample does not know about attr2.
-
-      >>> entries = getPermissionIds('attr2', klass=Sample)
-      >>> print entries['read_perm']
-      n/a
-      >>> print entries['write_perm']
-      n/a
-
-      Sample2 does not have a checker.
-
-      >>> entries = getPermissionIds('attr', klass=Sample2)
-      >>> entries['read_perm'] is None
-      True
-      >>> print entries['write_perm'] is None
-      True
-
-      Sample declares attr3 public.
-
-      >>> entries = getPermissionIds('attr3', klass=Sample)
-      >>> print entries['read_perm']
-      zope.Public
-      >>> print entries['write_perm']
-      zope.Public
-    """
+    """Get the permissions of an attribute."""
     assert (klass is _marker) != (checker is _marker)
     entry = {}
     
@@ -255,57 +124,7 @@
 
 
 def getFunctionSignature(func):
-    """Return the signature of a function or method.
-
-    The `func` argument *must* be a generic function or a method of a class. 
-
-    Examples::
-
-      >>> def func(attr, attr2=None):
-      ...     pass
-      >>> print getFunctionSignature(func)
-      (attr, attr2=None)
-
-      >>> def func(attr, **kw):
-      ...     pass
-      >>> print getFunctionSignature(func)
-      (attr, **kw)
-
-      >>> def func(attr, attr2=None, **kw):
-      ...     pass      
-      >>> print getFunctionSignature(func)
-      (attr, attr2=None, **kw)
-
-      >>> def func(*args, **kw):
-      ...     pass
-      >>> print getFunctionSignature(func)
-      (*args, **kw)
-
-      >>> def func(**kw):
-      ...     pass
-      >>> print getFunctionSignature(func)
-      (**kw)
-
-      >>> class Klass(object):
-      ...     def func(self, attr):
-      ...         pass
-      
-      >>> print getFunctionSignature(Klass.func)
-      (attr)
-
-      >>> class Klass(object):
-      ...     def func(self, attr, *args, **kw):
-      ...         pass
-      
-      >>> print getFunctionSignature(Klass.func)
-      (attr, *args, **kw)
-      
-      >>> try:
-      ...     getFunctionSignature('func')
-      ... except TypeError:
-      ...     print 'Argument not a function or method.'
-      Argument not a function or method.
-    """
+    """Return the signature of a function or method."""
     if not isinstance(func, (types.FunctionType, types.MethodType)):
         raise TypeError("func must be a function or method")
     
@@ -341,48 +160,7 @@
 
 
 def getPublicAttributes(obj):
-    """Return a list of public attribute names.
-
-    This excludes any attribute starting with '_'. The `obj` argument can be
-    either a classic class, type or instance of the previous two. Note that
-    the term "attributes" here includes methods and properties.
-
-    Examples::
-
-      >>> class Sample(object):
-      ...     attr = None
-      ...     def __str__(self):
-      ...         return ''
-      ...     def func(self):
-      ...         pass
-      ...     def _getAttr(self):
-      ...         return self.attr
-      ...     attr2 = property(_getAttr)
-      >>> class Sample2(object):
-      ...     attr = None
-      >>> class Sample3(Sample):
-      ...     attr3 = None
-      
-      >>> attrs = getPublicAttributes(Sample)
-      >>> attrs.sort()
-      >>> print attrs
-      ['attr', 'attr2', 'func']
-      
-      >>> attrs = getPublicAttributes(Sample())
-      >>> attrs.sort()
-      >>> print attrs
-      ['attr', 'attr2', 'func']
-      
-      >>> attrs = getPublicAttributes(Sample2)
-      >>> attrs.sort()
-      >>> print attrs
-      ['attr']
-      
-      >>> attrs = getPublicAttributes(Sample3)
-      >>> attrs.sort()
-      >>> print attrs
-      ['attr', 'attr2', 'attr3', 'func']
-    """
+    """Return a list of public attribute names."""
     attrs = []
     for attr in dir(obj):
         if attr.startswith('_'):
@@ -393,57 +171,7 @@
 
 def getInterfaceForAttribute(name, interfaces=_marker, klass=_marker,
                              asPath=True):
-    """Determine the interface in which an attribute is defined.
-
-    This function is nice, if you have an attribute name which you retrieved
-    from a class and want to know which interface requires it to be there.
-
-    Either 'interfaces' or 'klass' must be specified. If 'interfaces' is not
-    specified, the 'klass' is used to retrieve a list of
-    interfaces. 'interfaces' must be iterable.
-
-    `asPath` specifies whether the dotted name of the interface or the
-    interface object is returned.
-
-    If no match is found, ``None`` is returned.
-
-    Example::
-
-      >>> from zope.interface import Interface, Attribute
-      >>> class I1(Interface):
-      ...     attr = Attribute('attr')
-      >>> class I2(I1):
-      ...     def getAttr():
-      ...         '''get attr'''
-      >>> class Sample(object):
-      ...     implements(I2)
-
-      >>> getInterfaceForAttribute('attr', (I1, I2), asPath=False).getName()
-      'I1'
-      >>> getInterfaceForAttribute('getAttr', (I1, I2), asPath=False).getName()
-      'I2'
-      >>> getInterfaceForAttribute('attr', klass=Sample, asPath=False).getName()
-      'I1'
-      >>> getInterfaceForAttribute(
-      ...     'getAttr', klass=Sample, asPath=False).getName()
-      'I2'
-
-      >>> getInterfaceForAttribute('attr', (I1, I2))
-      'zope.app.apidoc.utilities.I1'
-
-      >>> getInterfaceForAttribute('attr2', (I1, I2)) is None
-      True
-      >>> getInterfaceForAttribute('attr2', klass=Sample) is None
-      True
-
-      >>> getInterfaceForAttribute('getAttr')
-      Traceback (most recent call last):
-      ValueError: need to specify interfaces or klass
-      >>> getInterfaceForAttribute('getAttr', interfaces=(I1,I2), klass=Sample)
-      Traceback (most recent call last):
-      ValueError: must specify only one of interfaces and klass
-
-    """
+    """Determine the interface in which an attribute is defined."""
     if (interfaces is _marker) and (klass is _marker):
         raise ValueError("need to specify interfaces or klass")
     if (interfaces is not _marker) and (klass is not _marker):
@@ -468,34 +196,7 @@
 
 
 def columnize(entries, columns=3):
-    """Place a list of entries into columns.
-
-    Examples::
-
-      >>> print columnize([1], 3)
-      [[1]]
-
-      >>> print columnize([1, 2], 3)
-      [[1], [2]]
-
-      >>> print columnize([1, 2, 3], 3)
-      [[1], [2], [3]]
-      
-      >>> print columnize([1, 2, 3, 4], 3)
-      [[1, 2], [3], [4]]
-
-      >>> print columnize([1], 2)
-      [[1]]
-
-      >>> print columnize([1, 2], 2)
-      [[1], [2]]
-
-      >>> print columnize([1, 2, 3], 2)
-      [[1, 2], [3]]
-      
-      >>> print columnize([1, 2, 3, 4], 2)
-      [[1, 2], [3, 4]]
-    """
+    """Place a list of entries into columns."""
     if len(entries)%columns == 0:
         per_col = len(entries)/columns
         last_full_col = columns
@@ -517,13 +218,13 @@
         columns.append(col)
     return columns
 
+
 _format_dict = {
     'plaintext': 'zope.source.plaintext',
     'structuredtext': 'zope.source.stx',
     'restructuredtext': 'zope.source.rest'
     }
     
-
 def getDocFormat(module):
     """Convert a module's __docformat__ specification to a renderer source
     id"""
@@ -532,6 +233,9 @@
 
 
 def renderText(text, module=None, format=None):
+    if not text:
+        return u''
+
     if module is not None:
         if isinstance(module, (str, unicode)):
             module = sys.modules.get(module, None)
@@ -542,9 +246,6 @@
         
     assert format in _format_dict.values()
 
-    if text:
-        source = zapi.createObject(format, text)
-        renderer = zapi.getMultiAdapter((source, TestRequest()))
-        return renderer.render()
-    else:
-        return u''
+    source = zapi.createObject(format, text)
+    renderer = zapi.getMultiAdapter((source, TestRequest()))
+    return renderer.render()

Added: Zope3/trunk/src/zope/app/apidoc/utilities.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilities.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilities.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,522 @@
+=======================
+Miscellaneous Utilities
+=======================
+
+The utilities module provides some useful helper functions and classes that
+make the work of the API doctool and inspection code easier. 
+
+  >>> from zope.app.apidoc import utilities
+
+
+`relativizePath(path)`
+----------------------
+
+When dealing with files, such as page templates and text files, and not with
+Python paths, it is necessary to keep track of the the absolute path of the
+file. However, for presentation purposes, the absolute path is inappropriate
+and we are commonly interested in the path starting at the Zope 3 root
+directory. This function attempts to remove the absolute path to the root
+directory and replaces it with "Zope3".
+
+  >>> import os
+  >>> path = os.path.join(utilities.BASEDIR, 'src', 'zope', 'README.txt')  
+
+  >>> utilities.BASEDIR in path
+  True
+
+  >>> path = utilities.relativizePath(path)
+
+  >>> utilities.BASEDIR in path
+  False
+
+  # Be kind to Windows users
+  >>> path.replace('\\', '/')
+  'Zope3/src/zope/README.txt'
+
+If the base path is not found in a particular path, the original path is
+returned:
+
+  >>> otherpath = 'foo/bar/blah.txt'
+  >>> utilities.relativizePath(otherpath)
+  'foo/bar/blah.txt'
+
+
+`ReadContainerBase` (Class)
+---------------------------
+
+This class serves as a base class for `IReadContainer` objects that minimizes
+the implementation of an `IReadContainer` to two methods, `get()` and
+`items()`, since the other methods can be implemented using these two.
+
+Note that this implementation might be very expensive for certain container,
+especially if collecting the items is of high order. However, there are many
+scenarios when one has a complete mapping already and simply want to persent
+it as an `IReadContainer`.
+
+Let's start by making a simple `IReadContainer` implementation using the
+class:
+
+  >>> class Container(utilities.ReadContainerBase):
+  ...     def get(self, key, default=None):
+  ...         return {'a': 1, 'b': 2}.get(key, default)
+  ...     def items(self):
+  ...         return [('a', 1), ('b', 2)]
+
+  >>> container = Container()
+
+Now we can use the methods. First `get()`
+
+  >>> container.get('a')
+  1
+  >>> container.get('c') is None
+  True
+  >>> container['b']
+  2
+
+and then `items()`
+
+  >>> container.items()
+  [('a', 1), ('b', 2)]
+  >>> container.keys()
+  ['a', 'b']
+  >>> container.values()
+  [1, 2]
+
+Then naturally, all the other methods work as well:
+
+  * `__getitem__(key)`
+  
+    >>> container['a']
+    1
+    >>> container['c']
+    Traceback (most recent call last):
+    ...
+    KeyError: 'c'
+  
+  * `__contains__(key)`
+  
+    >>> 'a' in container
+    True
+    >>> 'c' in container
+    False
+
+  * `keys()`
+
+    >>> container.keys()
+    ['a', 'b']
+
+  * `__iter__()`
+
+    >>> iterator = iter(container)
+    >>> iterator.next()
+    1
+    >>> iterator.next()
+    2
+    >>> iterator.next()
+    Traceback (most recent call last):
+    ...
+    StopIteration
+
+  * `values()`
+
+    >>> container.values()
+    [1, 2]
+
+  * `__len__()`
+
+    >>> len(container)
+    2
+
+
+`getPythonPath(obj)`
+--------------------
+
+Return the path of the object in standard Python dot-notation.
+
+This function makes only sense for objects that provide a name, since we
+cannot determine the path otherwise. Instances, for example, do not have a
+`__name__` attribute, so we would expect them to fail.
+
+For interfaces we simply get
+
+  >>> from zope.interface import Interface
+  >>> class ISample(Interface):
+  ...     pass
+
+  >>> utilities.getPythonPath(ISample)
+  '__builtin__.ISample'
+
+and for classes
+
+  >>> class Sample(object):
+  ...     def sample(self):
+  ...         pass
+
+  >>> utilities.getPythonPath(Sample.sample)
+  '__builtin__.Sample'
+
+One can also pass functions
+
+  >>> def sample():
+  ...     pass
+
+  >>> # Result is a bit strange due to doctests
+  >>> utilities.getPythonPath(sample)
+  'None.sample'
+
+and even methods. If a method is passed in, its class path is returned.
+
+  >>> utilities.getPythonPath(Sample.sample)
+  '__builtin__.Sample'
+
+Modules are another kind of objects that can return a python path:
+
+  >>> utilities.getPythonPath(utilities)
+  'zope.app.apidoc.utilities' 
+
+Passing in `None` returns `None`:
+
+  >>> utilities.getPythonPath(None)
+
+Clearly, instance lookups should fail:
+
+  >>> utilities.getPythonPath(Sample())
+  Traceback (most recent call last):
+  ...
+  AttributeError: 'Sample' object has no attribute '__name__'
+
+
+`getPermissionIds(name, checker=_marker, klass=_marker)`
+--------------------------------------------------------
+
+Get the permissions of a class attribute. The attribute is specified by name.
+
+Either the `klass` or the `checker` argument must be specified. If the class
+is specified, then the checker for it is looked up. Furthermore, this function
+only works with `INameBasedChecker` checkers. If another checker is found,
+``None`` is returned for the permissions.
+
+We start out by defining the class and then the checker for it:
+
+  >>> from zope.security.checker import Checker, defineChecker
+  >>> from zope.security.checker import CheckerPublic
+
+  >>> class Sample(object):
+  ...     attr = 'value'
+  ...     attr3 = 'value3'
+
+  >>> class Sample2(object):
+  ...      pass
+
+  >>> checker = Checker({'attr': 'zope.Read', 'attr3': CheckerPublic},
+  ...                   {'attr': 'zope.Write', 'attr3': CheckerPublic}) 
+  >>> defineChecker(Sample, checker)
+
+Now let's see how this function works:
+
+  >>> entries = utilities.getPermissionIds('attr', klass=Sample)
+  >>> entries['read_perm']
+  'zope.Read'
+  >>> entries['write_perm']
+  'zope.Write'
+
+  >>> from zope.security.checker import getCheckerForInstancesOf
+  >>> entries = utilities.getPermissionIds('attr', 
+  ...                                      getCheckerForInstancesOf(Sample))
+  >>> entries['read_perm']
+  'zope.Read'
+  >>> entries['write_perm']
+  'zope.Write'
+
+The `Sample` class does not know about the `attr2` attribute:
+
+  >>> entries = utilities.getPermissionIds('attr2', klass=Sample)
+  >>> print entries['read_perm']
+  n/a
+  >>> print entries['write_perm']
+  n/a
+
+The `Sample2` class does not have a checker:
+
+  >>> entries = utilities.getPermissionIds('attr', klass=Sample2)
+  >>> entries['read_perm'] is None
+  True
+  >>> print entries['write_perm'] is None
+  True
+
+Finally, the `Sample` class' `attr3` attribute is public:
+
+  >>> entries = utilities.getPermissionIds('attr3', klass=Sample)
+  >>> print entries['read_perm']
+  zope.Public
+  >>> print entries['write_perm']
+  zope.Public
+
+
+`getFunctionSignature(func)`
+----------------------------
+
+Return the signature of a function or method. The `func` argument *must* be a
+generic function or a method of a class.
+
+First, we get the signature of a function that has a specific positional and
+keyword argument:
+
+  >>> def func(attr, attr2=None):
+  ...     pass
+  >>> utilities.getFunctionSignature(func)
+  '(attr, attr2=None)'
+
+Here is a function that has an unspecified amount of keyword arguments:
+
+  >>> def func(attr, **kw):
+  ...     pass
+  >>> utilities.getFunctionSignature(func)
+  '(attr, **kw)'
+
+And here we mix specified and unspecified keyword arguments:
+
+  >>> def func(attr, attr2=None, **kw):
+  ...     pass      
+  >>> utilities.getFunctionSignature(func)
+  '(attr, attr2=None, **kw)'
+
+In the next example we have unspecified positional and keyword arguments:
+
+  >>> def func(*args, **kw):
+  ...     pass
+  >>> utilities.getFunctionSignature(func)
+  '(*args, **kw)'
+
+And finally an example, where we have on unspecified keyword arguments without
+any positional arguments:
+
+  >>> def func(**kw):
+  ...     pass
+  >>> utilities.getFunctionSignature(func)
+  '(**kw)'
+
+Next we test whether the signature is correctly determined for class
+methods. Note that the `self` argument is removed from the signature, since it
+is not essential for documentation.
+
+We start out with a simple positional argument:
+
+  >>> class Klass(object):
+  ...     def func(self, attr):
+  ...         pass
+  >>> utilities.getFunctionSignature(Klass.func)
+  '(attr)'
+
+Next we have specific and unspecified positional arguments as well as
+unspecified keyword arguments:
+
+  >>> class Klass(object):
+  ...     def func(self, attr, *args, **kw):
+  ...         pass
+  >>> utilities.getFunctionSignature(Klass.func)
+  '(attr, *args, **kw)'
+
+If you do not pass a function or method to the function, it will fail:
+  
+  >>> utilities.getFunctionSignature('func')
+  Traceback (most recent call last):
+  ...
+  TypeError: func must be a function or method
+
+`getPublicAttributes(obj)`
+--------------------------
+
+Return a list of public attribute names for a given object.
+
+This excludes any attribute starting with '_', which includes attributes of
+the form `__attr__`, which are commonly considered public, but they are so
+special that they are excluded. The `obj` argument can be either a classic
+class, type or instance of the previous two. Note that the term "attributes"
+here includes methods and properties.
+
+First we need to create a class with some attributes, properties and methods:
+
+  >>> class Sample(object):
+  ...     attr = None
+  ...     def __str__(self):
+  ...         return ''
+  ...     def func(self):
+  ...         pass
+  ...     def _getAttr(self):
+  ...         return self.attr
+  ...     attr2 = property(_getAttr)
+  
+We can simply pass in the class and get the public attributes:
+
+  >>> attrs = utilities.getPublicAttributes(Sample)
+  >>> attrs.sort()
+  >>> attrs
+  ['attr', 'attr2', 'func']
+
+But an instance of that class will work as well.
+
+  >>> attrs = utilities.getPublicAttributes(Sample())
+  >>> attrs.sort()
+  >>> attrs
+  ['attr', 'attr2', 'func']
+  
+The function will also take inheritance into account and return all inherited
+attributes as well:
+
+  >>> class Sample2(Sample):
+  ...     attr3 = None
+
+  >>> attrs = utilities.getPublicAttributes(Sample2)
+  >>> attrs.sort()
+  >>> attrs
+  ['attr', 'attr2', 'attr3', 'func']
+
+
+`getInterfaceForAttribute(name, interfaces=_marker, klass=_marker, asPath=True)`
+--------------------------------------------------------------------------------
+
+Determine the interface in which an attribute is defined. This function is
+nice, if you have an attribute name which you retrieved from a class and want
+to know which interface requires it to be there.
+
+Either the `interfaces` or `klass` argument must be specified. If `interfaces`
+is not specified, the `klass` is used to retrieve a list of
+interfaces. `interfaces` must be iterable.
+
+`asPath` specifies whether the dotted name of the interface or the interface
+object is returned.
+
+First, we need to create some interfaces and a class that implements them:
+
+  >>> from zope.interface import Interface, Attribute, implements
+  >>> class I1(Interface):
+  ...     attr = Attribute('attr')
+
+  >>> class I2(I1):
+  ...     def getAttr():
+  ...         '''get attr'''
+
+  >>> class Sample(object):
+  ...     implements(I2)
+
+First we check whether an aatribute can be found in a list of interfaces:
+
+  >>> utilities.getInterfaceForAttribute('attr', (I1, I2), asPath=False)
+  <InterfaceClass __builtin__.I1>
+  >>> utilities.getInterfaceForAttribute('getAttr', (I1, I2), asPath=False)
+  <InterfaceClass __builtin__.I2>
+
+Now we are repeating the same lookup, but using the class, instead of a list
+of interfaces:
+
+  >>> utilities.getInterfaceForAttribute('attr', klass=Sample, asPath=False)
+  <InterfaceClass __builtin__.I1>
+  >>> utilities.getInterfaceForAttribute('getAttr', klass=Sample, asPath=False)
+  <InterfaceClass __builtin__.I2>
+
+By default, `asPath` is `True`, which means the path of the interface is
+returned:
+
+  >>> utilities.getInterfaceForAttribute('attr', (I1, I2))
+  '__builtin__.I1'
+
+If no match is found, ``None`` is returned.
+
+  >>> utilities.getInterfaceForAttribute('attr2', (I1, I2)) is None
+  True
+  >>> utilities.getInterfaceForAttribute('attr2', klass=Sample) is None
+  True
+
+If both, the `interfaces` and `klass` argument are missing, raise an error:
+
+  >>> utilities.getInterfaceForAttribute('getAttr')
+  Traceback (most recent call last):
+  ...
+  ValueError: need to specify interfaces or klass
+
+Similarly, it does not make sense if both are specified:
+
+  >>> utilities.getInterfaceForAttribute('getAttr', interfaces=(I1,I2), 
+  ...                                    klass=Sample)
+  Traceback (most recent call last):
+  ...
+  ValueError: must specify only one of interfaces and klass
+
+
+`columnize(entries, columns=3)`
+-------------------------------
+
+This function places a list of entries into columns.
+
+Here are some examples:
+
+  >>> utilities.columnize([1], 3)
+  [[1]]
+
+  >>> utilities.columnize([1, 2], 3)
+  [[1], [2]]
+
+  >>> utilities.columnize([1, 2, 3], 3)
+  [[1], [2], [3]]
+  
+  >>> utilities.columnize([1, 2, 3, 4], 3)
+  [[1, 2], [3], [4]]
+
+  >>> utilities.columnize([1], 2)
+  [[1]]
+
+  >>> utilities.columnize([1, 2], 2)
+  [[1], [2]]
+
+  >>> utilities.columnize([1, 2, 3], 2)
+  [[1, 2], [3]]
+  
+  >>> utilities.columnize([1, 2, 3, 4], 2)
+  [[1, 2], [3, 4]]
+
+
+`getDocFormat(module)`
+----------------------
+
+This function inspects a module to determine the supported documentation
+format. The function returns a valid renderer source factory id. 
+
+If the `__docformat__` module attribute is specified, its value will be used
+to look up the factory id:
+
+  >>> from zope.app.apidoc import apidoc
+  >>> utilities.getDocFormat(apidoc)
+  'zope.source.rest'
+
+By default structured text is returned:
+
+  >>> from zope.app.apidoc import tests
+  >>> utilities.getDocFormat(tests)
+  'zope.source.stx'
+
+This is a sensible default, since we only decided later in development to
+endorse restructured text, so that many files are still in the structured text
+format. All converted and new modules will have the `__docformat__` attribute. 
+
+
+`renderText(text, module=None, format=None)`
+--------------------------------------------
+
+A function that quickly renders the given text using the specified format.
+
+If the `module` argument is specified, the function will try to determine the
+format using the module. If the `format` argument is given, it is simply
+used. Clearly, you cannot specify both, the `module` and `format` argument.
+
+You specify the format as follows:
+
+  >>> utilities.renderText('Hello!\n', format='zope.source.rest')
+  u'<div class="document">\nHello!</div>\n'
+
+Note that the format string must be a valid source factory id; if the factory
+id is not a match, 'zope.source.stx' is used. Thus, specifying the module is
+often safer (if available):
+
+  >>> utilities.renderText('Hello!\n', module=apidoc)
+  u'<div class="document">\nHello!</div>\n'
\ No newline at end of file


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

Added: Zope3/trunk/src/zope/app/apidoc/utilitymodule/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/README.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/README.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,78 @@
+==================================
+The Utilities Documentation Module
+==================================
+
+This documentation module organizes all registered utilities by their provided
+interface and then by the name of the utility.
+
+`UtilityModule` class
+---------------------
+
+This class represents the documentation of all utility interfaces. The items
+of the container are all `UtilityInterface` instances.
+
+Let's start by creating a utility documentation module:
+
+  >>> from zope.app.apidoc.utilitymodule.utilitymodule import UtilityModule
+  >>> module = UtilityModule()
+
+To make the documentation module useful, we have to register a utility, so why
+not the documentation module itself?
+
+  >>> from zope.app.apidoc.interfaces import IDocumentationModule
+  >>> from zope.app.testing import ztapi
+  >>> ztapi.provideUtility(IDocumentationModule, module, 'Utility')
+
+Now we can get a single utility interface by path: 
+
+  >>> module.get('zope.app.apidoc.interfaces.IDocumentationModule')
+  <zope.app.apidoc.utilitymodule.utilitymodule.UtilityInterface ...>
+
+and list all available interfaces:
+
+  >>> module.items()
+  [('zope.app.apidoc.interfaces.IDocumentationModule', 
+    <zope.app.apidoc.utilitymodule.utilitymodule.UtilityInterface ...>), 
+   ('zope.security.interfaces.IPermission', 
+    <zope.app.apidoc.utilitymodule.utilitymodule.UtilityInterface ...>)]
+
+
+`UtilityInterface` class
+------------------------
+
+Representation of an interface a utility provides.
+
+First we create a utility interface documentation instance:
+
+  >>> from zope.app.apidoc.utilitymodule.utilitymodule import UtilityInterface
+  >>> ut_iface = UtilityInterface(
+  ...     module,
+  ...     'zope.app.apidoc.interfaces.IDocumentationModule',
+  ...     IDocumentationModule) 
+
+Now we can get the utility:
+
+  >>> ut_iface.get('Utility').component
+  <zope.app.apidoc.utilitymodule.utilitymodule.UtilityModule object at ...>
+
+Unnamed utilities are special, since they can be looked up in different ways:
+
+  >>> ztapi.provideUtility(IDocumentationModule, module, '')
+
+  >>> ut_iface.get('').component
+  <zope.app.apidoc.utilitymodule.utilitymodule.UtilityModule object at ...>
+
+  >>> from zope.app.apidoc.utilitymodule.utilitymodule import NONAME
+  >>> ut_iface.get(NONAME).component
+  <zope.app.apidoc.utilitymodule.utilitymodule.UtilityModule object at ...>  
+
+If you try to get a non-existent utility, `None` is returned:
+
+  >>> ut_iface.get('foo') is None
+  True
+
+You can get a list of available utilities as well, of course:
+
+  >>> ut_iface.items()
+  [('Utility', <zope.app.apidoc.utilitymodule.utilitymodule.Utility ...>),
+   ('__noname__', <zope.app.apidoc.utilitymodule.utilitymodule.Utility ...>)]


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

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/__init__.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/__init__.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,166 +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.
-#
-##############################################################################
-"""Utility Documentation Module
-
-$Id$
-"""
-__docformat__ = 'restructuredtext'
-
-from zope.component.site import UtilityRegistration
-from zope.interface import implements
-
-from zope.app import zapi
-from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.app.component import queryNextSiteManager
-from zope.app.location.interfaces import ILocation
-from zope.app.apidoc.interfaces import IDocumentationModule
-from zope.app.apidoc.utilities import ReadContainerBase, getPythonPath
-
-# Constant used when the utility has no name
-NONAME = '__noname__'
-
-class Utility(object):
-    """Representation of a utility for the API Documentation"""
-
-    implements(ILocation)
-    
-    def __init__(self, parent, reg):
-        """Initialize Utility object."""
-        self.__parent__ = parent
-        self.__name__ = reg.name or NONAME
-        self.registration = reg
-        self.interface = reg.provided
-        self.component = reg.component
-        # Handle local and global utility registrations
-        self.doc = hasattr(reg, 'doc') and reg.doc or ''
-
-
-class UtilityInterface(ReadContainerBase):
-    r"""Representation of an interface a utility provides.
-
-    Demonstration::
-
-      >>> from zope.app.apidoc.interfaces import IDocumentationModule
-
-      >>> id = 'zope.app.apidoc.interfaces.IDocumentationModule'
-      >>> ut_iface = UtilityInterface(UtilityModule(), id,
-      ...                             IDocumentationModule) 
-
-      >>> ut_iface.get('Classes').component.__class__.__name__
-      'ClassModule'
-
-      >>> ut_iface.get('').component.__class__.__name__
-      'InterfaceModule'
-
-      >>> ut_iface.get(NONAME).component.__class__.__name__
-      'InterfaceModule'
-
-      >>> ut_iface.get('foo') is None
-      True
-
-      >>> print '\n'.join([id for id, iface in ut_iface.items()])
-      Classes
-      __noname__
-    """
-
-    implements(ILocation)
-
-    def __init__(self, parent, name, interface):
-        self.__parent__ = parent
-        self.__name__ = name
-        self.interface = interface
-
-    def get(self, key, default=None):
-        """See zope.app.container.interfaces.IReadContainer"""
-        sm = zapi.getGlobalSiteManager()
-        if key == NONAME:
-            key = ''
-        utils = [Utility(self, reg)
-                 for reg in sm.registrations()
-                 if zapi.isinstance(reg, UtilityRegistration) and \
-                     reg.name == key and reg.provided == self.interface]
-        return utils and utils[0] or default
-
-    def items(self):
-        """See zope.app.container.interfaces.IReadContainer"""
-        sm = zapi.getGlobalSiteManager()
-        items = [(reg.name or NONAME, Utility(self, reg))
-                 for reg in sm.registrations()
-                 if zapi.isinstance(reg, UtilityRegistration) and \
-                     self.interface == reg.provided]
-        items.sort()
-        return items
-
-
-class UtilityModule(ReadContainerBase):
-    r"""Represent the Documentation of all Interfaces.
-
-    This documentation is implemented using a simple `IReadContainer`. The
-    items of the container are all factories listed in the closest
-    site manager and above.
-
-    Demonstration::
-
-      >>> module = UtilityModule()
-      >>> ut_iface = module.get(
-      ...     'zope.app.apidoc.interfaces.IDocumentationModule')
-
-      >>> ut_iface.interface.getName()
-      'IDocumentationModule'
-
-      >>> print '\n'.join([id for id, iface in module.items()])
-      zope.app.apidoc.interfaces.IDocumentationModule
-      zope.security.interfaces.IPermission
-    """
-
-    implements(IDocumentationModule)
-
-    # See zope.app.apidoc.interfaces.IDocumentationModule
-    title = _('Utilities')
-
-    # See zope.app.apidoc.interfaces.IDocumentationModule
-    description = _("""
-    Utilities are also nicely registered in a site manager, so that it is easy
-    to create a listing of available utilities. A utility is identified by the
-    providing interface and a name, which can be empty. The menu provides you
-    with a list of interfaces that utilities provide and as sub-items the
-    names of the various implementations.
-
-    Again, the documentation of a utility lists all the attributes/fields and
-    methods the utility provides and provides a link to the implementation.
-    """)
-
-    def get(self, key, default=None):
-        parts = key.split('.')
-        try:
-            mod = __import__('.'.join(parts[:-1]), {}, {}, ('*',))
-        except ImportError:
-            return default
-        else:
-            return UtilityInterface(self, key, getattr(mod, parts[-1], default))
-
-    def items(self):
-        sm = zapi.getSiteManager()
-        ifaces = {}
-        while sm is not None:
-            for reg in sm.registrations():
-                if isinstance(reg, UtilityRegistration):
-                    path = getPythonPath(reg.provided)
-                    ifaces[path] = UtilityInterface(self, path, reg.provided)
-            sm = queryNextSiteManager(sm)
-
-        items = ifaces.items()
-        items.sort(lambda x, y: cmp(x[0].split('.')[-1], y[0].split('.')[-1]))
-        return items
-
+# Make a package

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -21,89 +21,29 @@
 from zope.app.location import LocationProxy
 from zope.app.apidoc.ifacemodule.browser import InterfaceDetails
 from zope.app.apidoc.utilities import getPythonPath
-from zope.app.apidoc.utilitymodule import NONAME, Utility, UtilityInterface
+from zope.app.apidoc.utilitymodule.utilitymodule import NONAME, Utility
+from zope.app.apidoc.utilitymodule.utilitymodule import UtilityInterface
 
 class UtilityDetails(object):
     """Utility Details View."""
 
     def getName(self):
-        """Get the name of the utility.
-
-        Return the string ``no name``, if the utility has no name.
-
-        Examples::
-
-          >>> def makeRegistration(name):
-          ...     return type('RegistrationStub', (),
-          ...                 {'name': name, 'provided': None,
-          ...                  'component': None, 'doc': ''})()
-
-          >>> details = UtilityDetails()
-          >>> details.context = Utility(None, makeRegistration('myname'))
-          >>> details.getName()
-          'myname'
-
-          >>> details.context = Utility(None, makeRegistration(NONAME))
-          >>> details.getName()
-          'no name'
-        """
+        """Get the name of the utility."""
         name = zapi.name(self.context)
         if name == NONAME:
             return 'no name'
         return name
 
     def getInterface(self):
-        """Return the interface the utility provides.
-
-        Example::
-
-          >>> from tests import getDetailsView
-          >>> details = getDetailsView()
-
-          >>> iface = details.getInterface()
-          >>> iface.getId()
-          'zope.app.apidoc.interfaces.IDocumentationModule'
-        """ 
+        """Return the interface the utility provides.""" 
         schema = LocationProxy(self.context.interface,
                                self.context,
                                getPythonPath(self.context.interface))
-        details = InterfaceDetails()
-        details.context = schema
-        details.request = self.request
-        
+        details = InterfaceDetails(schema, self.request)
         return details
 
     def getComponent(self):
-        """Return the python path of the implementation class.
-
-        Examples::
-
-          >>> from zope.app.apidoc.utilitymodule import Utility
-          >>> from zope.app.apidoc.tests import pprint
-
-          >>> def makeRegistration(name, component):
-          ...     return type(
-          ...         'RegistrationStub', (),
-          ...         {'name': name, 'provided': None,
-          ...          'component': component, 'doc': ''})()
-
-          >>> class Foo(object):
-          ...     pass
-
-          >>> class Bar(object):
-          ...     pass
-
-          >>> details = UtilityDetails()
-          >>> details.context = Utility(None, makeRegistration('', Foo()))
-          >>> pprint(details.getComponent())
-          [('path', 'zope.app.apidoc.utilitymodule.browser.Foo'),
-           ('url', 'zope/app/apidoc/utilitymodule/browser/Foo')]
-
-          >>> details.context = Utility(None, makeRegistration('', Bar()))
-          >>> pprint(details.getComponent())
-          [('path', 'zope.app.apidoc.utilitymodule.browser.Bar'),
-           ('url', 'zope/app/apidoc/utilitymodule/browser/Bar')]
-        """
+        """Return the python path of the implementation class."""
         # We could use `type()` here, but then we would need to remove the
         # security proxy from the component. This is easier and also supports
         # old-style classes 
@@ -112,53 +52,10 @@
         return {'path': getPythonPath(klass),
                 'url':  getPythonPath(klass).replace('.', '/')}
 
+
 class Menu(object):
-    """Menu View Helper Class
+    """Menu View Helper Class"""
 
-    A class that helps building the menu. The menu_macros expects the menu view
-    class to have the `getMenuTitle(node)` and `getMenuLink(node)` methods
-    implemented. `node` is a `zope.app.tree.node.Node` instance.
-
-    Examples::
-
-      >>> from zope.app.tree.node import Node 
-      >>> from zope.app.apidoc.utilitymodule import Utility, UtilityInterface
-      >>> from zope.app.apidoc.tests import Root
-      >>> menu = Menu()
-
-      >>> def makeRegistration(name):
-      ...     return type('RegistrationStub', (),
-      ...                 {'name': name, 'provided': None,
-      ...                  'component': None, 'doc': ''})()
-
-      Get menu title and link for a utility interface
-
-      >>> uiface = UtilityInterface(Root(), 'foo.bar.iface', None)
-      >>> node = Node(uiface)
-      >>> menu.getMenuTitle(node)
-      'iface'
-      >>> menu.getMenuLink(node)
-      '../Interface/foo.bar.iface/apiindex.html'
-
-      Get menu title and link for a utility with a name
-
-      >>> util = Utility(uiface, makeRegistration('FooBar'))
-      >>> node = Node(util)
-      >>> menu.getMenuTitle(node)
-      'FooBar'
-      >>> menu.getMenuLink(node)
-      './foo.bar.iface/FooBar/index.html'
-
-      Get menu title and link for a utility without a name
-
-      >>> util = Utility(uiface, makeRegistration(None))
-      >>> node = Node(util)
-      >>> menu.getMenuTitle(node)
-      'no name'
-      >>> menu.getMenuLink(node)
-      './foo.bar.iface/__noname__/index.html'
-    """
-
     def getMenuTitle(self, node):
         """Return the title of the node that is displayed in the menu."""
         obj = node.context

Added: Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,154 @@
+================================
+Utilities Menu and Details Views
+================================
+
+
+`Menu` class
+------------
+
+This is a class that helps building the menu. The `menu_macros` expect the menu
+view class to have the `getMenuTitle(node)` and `getMenuLink(node)` methods
+implemented. `node` is a `zope.app.tree.node.Node` instance. 
+
+Let's start by creating the menu:
+
+  >>> from zope.app.apidoc.utilitymodule.browser import Menu
+  >>> menu = Menu()
+
+Now we want to get the menu title and link for a utility interface. To do that
+we first have to create a utility interface
+
+  >>> from zope.app.apidoc.tests import Root
+  >>> from zope.app.apidoc.utilitymodule.utilitymodule import UtilityInterface
+  >>> uiface = UtilityInterface(Root(), 'foo.bar.iface', None)
+
+and then wrap it in a node:
+
+  >>> from zope.app.tree.node import Node 
+  >>> node = Node(uiface)
+
+You can now get the title and link from the menu:
+
+  >>> menu.getMenuTitle(node)
+  'iface'
+  >>> menu.getMenuLink(node)
+  '../Interface/foo.bar.iface/apiindex.html'
+
+Next, let's get the menu title and link for a utility with a name. We first
+have to create a utility registration
+
+  >>> foobar_reg = type(
+  ...     'RegistrationStub', (),
+  ...     {'name': 'FooBar', 'provided': None, 
+  ...      'component': None, 'doc': ''})()
+
+which is then wrapped in a `Utility` documentation class and then in a node:
+
+  >>> from zope.app.apidoc.utilitymodule.utilitymodule import Utility
+  >>> util = Utility(uiface, foobar_reg)
+  >>> node = Node(util)
+
+We can now ask the menu to give us the tile and link for the utility:
+
+  >>> menu.getMenuTitle(node)
+  'FooBar'
+  >>> menu.getMenuLink(node)
+  './foo.bar.iface/FooBar/index.html'
+
+Finally, we get menu title and link for a utility without a name:
+
+  >>> from zope.app.apidoc.utilitymodule.utilitymodule import NONAME
+  >>> noname_reg = type(
+  ...     'RegistrationStub', (),
+  ...     {'name': NONAME, 'provided': None, 
+  ...      'component': None, 'doc': ''})()
+
+  >>> util = Utility(uiface, noname_reg)
+  >>> node = Node(util)
+  >>> menu.getMenuTitle(node)
+  'no name'
+  >>> menu.getMenuLink(node)
+  './foo.bar.iface/__noname__/index.html'
+
+
+`UtilityDetails` class
+----------------------
+
+This class provides presentation-ready data about a particular utility.
+
+`getName()`
+-----------
+    
+Get the name of the utility.
+
+  >>> from zope.app.apidoc.utilitymodule.browser import UtilityDetails
+  >>> details = UtilityDetails()
+  >>> details.context = Utility(None, foobar_reg)
+  >>> details.getName()
+  'FooBar'
+
+Return the string ``no name``, if the utility has no name.
+
+  >>> details.context = Utility(None, noname_reg)
+  >>> details.getName()
+  'no name'
+
+
+`getInterface()`
+----------------
+
+Return the interface details view for the interface the utility provides.
+
+Let's start by creating the utility interface and building a utility
+registration:
+
+  >>> from zope.interface import Interface
+  >>> class IBlah(Interface):
+  ...     pass
+
+  >>> blah_reg = type(
+  ...     'RegistrationStub', (),
+  ...     {'name': 'Blah', 'provided': IBlah, 
+  ...      'component': None, 'doc': ''})()
+
+Then we wrap the registration in the utility documentation class and create
+the details view:
+
+  >>> details = UtilityDetails()
+  >>> details.context = Utility(None, blah_reg)
+  >>> details.request = None
+
+Now that we have the details view, we can look up the interface's detail view
+and get the id (for example):
+
+  >>> iface = details.getInterface()
+  >>> iface.getId()
+  '__builtin__.IBlah'
+
+
+`getComponent()`
+----------------
+
+Return the Python path and a code browser URL path of the implementation
+class.
+
+This time around we create the utility class and put it into a utility
+registration:
+
+  >>> class Foo(object):
+  ...     pass
+
+  >>> foo_reg = type(
+  ...     'RegistrationStub', (),
+  ...     {'name': '', 'provided': None, 'component': Foo(), 'doc': ''})()
+
+Then we create a utility documentation class and its details view:
+
+  >>> details = UtilityDetails()
+  >>> details.context = Utility(None, foo_reg)
+
+Now we can get the component information:
+
+  >>> pprint(details.getComponent())
+  {'path': '__builtin__.Foo',
+   'url': '__builtin__/Foo'}


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

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml	2005-02-23 22:22:48 UTC (rev 29269)
@@ -2,34 +2,34 @@
   xmlns="http://namespaces.zope.org/zope"
   xmlns:browser="http://namespaces.zope.org/browser">
 
-  <class class=".UtilityModule">
+  <class class=".utilitymodule.UtilityModule">
     <allow interface="zope.app.apidoc.interfaces.IDocumentationModule" />
     <allow interface="zope.app.container.interfaces.IReadContainer" />
   </class>
 
-  <class class=".Utility">
+  <class class=".utilitymodule.Utility">
     <allow attributes="registration interface component doc" />
   </class>
 
-  <class class=".UtilityInterface">
+  <class class=".utilitymodule.UtilityInterface">
     <allow interface="zope.app.container.interfaces.IReadContainer" />
     <allow attributes="interface" />
   </class>
 
   <utility
       provides="zope.app.apidoc.interfaces.IDocumentationModule"
-      factory=".UtilityModule"
+      factory=".utilitymodule.UtilityModule"
       name="Utility" />
 
   <browser:page
-      for=".UtilityModule"
+      for=".utilitymodule.UtilityModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.Menu"
       name="menu.html"
       template="menu.pt" />
 
   <browser:page
-      for=".Utility"
+      for=".utilitymodule.Utility"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.UtilityDetails"
       name="index.html"

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/ftests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/ftests.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/ftests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -35,16 +35,18 @@
         response = self.publish(
             '/++apidoc++/Utility/'
             'zope.app.apidoc.interfaces.IDocumentationModule/'
-            'Class/index.html',
+            'Utility/index.html',
             basic='mgr:mgrpw')
         self.assertEqual(response.getStatus(), 200)
         body = response.getBody()
-        self.assert_(body.find('zope.app.apidoc.classmodule.ClassModule') > 0)
+        self.assert_(
+            body.find(
+               'zope.app.apidoc.utilitymodule.utilitymodule.UtilityModule') > 0)
         self.checkForBrokenLinks(
             body,
             '/++apidoc++/Utility/'
             'zope.app.apidoc.interfaces.IDocumentationModule/'
-            'Class/index.html',
+            'Utility/index.html',
             basic='mgr:mgrpw')
 
 

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,6 +1,7 @@
 <html metal:use-macro="views/apidoc_macros/details">
 <body metal:fill-slot="contents"
-      tal:define="iface view/getInterface">
+      tal:define="iface view/getInterface;
+                  rootURL iface/apidocRoot">
 
   <h1 class="details-header">
     <a href=""
@@ -17,7 +18,7 @@
       <span i18n:translate="">Component:</span>
       <a href=""
          tal:attributes="href 
-             string:../../../Class/${component/url}/index.html"
+             string:../../../Code/${component/url}/index.html"
          tal:content="component/path" /></h3>
   </div>
 
@@ -27,74 +28,14 @@
     </div>
   </div>
 
-  <h2 class="details-section" i18n:translate="">Attributes/Fields</h2>
+  <tal:block define="attributes iface/getAttributes;
+                     fields iface/getFields">
+    <metal:block use-macro="context/@@interface_macros/attributes_fields" />
+  </tal:block>
 
-  <div class="indent"
-      tal:define="attributes iface/getAttributes;
-                  fields iface/getFields">
+  <tal:block define="methods iface/getMethods">
+    <metal:block use-macro="context/@@interface_macros/methods" />
+  </tal:block>
 
-  <ul class="attr-list" 
-      tal:condition="python: attributes or fields">
-  
-    <li tal:repeat="attr attributes">
-      <b><code tal:content="attr/name">attr</code></b>
-      	<span i18n:translate="">(Attribute)</span><br>
-      <div class="inline-documentation" tal:content="structure attr/doc">
-        attr desc
-      </div>      
-    </li>
-
-    <li tal:repeat="field fields">
-      <b tal:attributes="class field/required_css">
-        <code tal:content="field/name">field</code>
-        <span tal:condition="field/required">*</span>
-      </b>
-      - <a href=""
-           tal:attributes="
-               href string:../../../Interface/${field/iface/id}/apiindex.html">
-          <code tal:content="field/iface/name">IField</code></a>
-      (<span i18n:translate="">default</span> =
-            <code tal:content="field/default" />)<br />
-      <div tal:content="structure field/title" class="field-title">title</div>
-      <span tal:content="field/description">field desc</span>      
-    </li>
-
-  </ul>
-
-  <p tal:condition="python: not (attributes or fields)">
-    <em i18n:translate="">There are no attributes or fields specified.</em>
-  </p>
-
-  </div>
-
-
-
-  <h2 class="details-section" i18n:translate="">Methods</h2>
-
-  <div class="indent"
-       tal:define="methods iface/getMethods">
-
-  <ul class="attr-list" tal:condition="methods">
-    <li tal:repeat="method methods">
-      <b><code 
-          tal:content="string:${method/name}${method/signature}" />
-      </b><br>
-      <div class="inline-documentation" tal:content="structure method/doc">
-        method desc
-      </div>      
-    </li>
-  </ul>
-
-  <p tal:condition="not: methods">
-    <em i18n:translate="">There are no methods or fields specified.</em>
-  </p>
-
-  </div>
-
-  <p>
-    <em><b class="required">*</b> =
-    <span i18n:translate="">required</span></em>
-  </p>
-
 </body>
 </html>

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/tests.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/tests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -16,62 +16,37 @@
 $Id$
 """
 import unittest
+from zope.testing import doctest, doctestunit
 
-from zope.publisher.browser import TestRequest
-from zope.testing.doctestunit import DocTestSuite
-
-from zope.app import zapi
+from zope.app.location.traversing import LocationPhysicallyLocatable
 from zope.app.testing import placelesssetup, ztapi
-
-from zope.app.apidoc.interfaces import IDocumentationModule
-from zope.app.apidoc.tests import Root
-from zope.app.apidoc.ifacemodule import InterfaceModule
-from zope.app.apidoc.classmodule import ClassModule
-from zope.app.apidoc.utilitymodule import UtilityModule, Utility
-from browser import UtilityDetails
-
+from zope.app.traversing.interfaces import IPhysicallyLocatable
 from zope.app.tree.interfaces import IUniqueId
 from zope.app.tree.adapters import LocationUniqueId 
 
-from zope.app.traversing.interfaces import IPhysicallyLocatable
-from zope.app.location.traversing import LocationPhysicallyLocatable
 
-
 def setUp(test):
     placelesssetup.setUp()
-    gsm = zapi.getGlobalSiteManager()
-    gsm.provideUtility(IDocumentationModule, InterfaceModule(), '')
-    gsm.provideUtility(IDocumentationModule, ClassModule(), 'Classes')
 
     ztapi.provideAdapter(None, IUniqueId, LocationUniqueId)
     ztapi.provideAdapter(None, IPhysicallyLocatable,
                          LocationPhysicallyLocatable)
 
 
-def makeRegistration(name, interface, component):
-    return type('RegistrationStub', (),
-                {'name': name, 'provided': interface,
-                 'component': component, 'doc': ''})()
-
-def getDetailsView():
-    utils = UtilityModule()
-    utils.__parent__ = Root
-    utils.__name__ = 'Utility'
-    util = Utility(
-        utils,
-        makeRegistration('Classes', IDocumentationModule, ClassModule()))
-    details = UtilityDetails()
-    details.context = util
-    details.request = TestRequest()
-    return details
-
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.app.apidoc.utilitymodule',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
-        DocTestSuite('zope.app.apidoc.utilitymodule.browser',
-                     setUp=setUp, tearDown=placelesssetup.tearDown),
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE|
+                                         doctest.ELLIPSIS),
+        doctest.DocFileSuite('browser.txt',
+                             setUp=setUp,
+                             tearDown=placelesssetup.tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
         ))
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(default="test_suite")

Added: Zope3/trunk/src/zope/app/apidoc/utilitymodule/utilitymodule.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/utilitymodule.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/utilitymodule.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,123 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Utility Documentation Module
+
+$Id: __init__.py 29199 2005-02-17 22:38:55Z srichter $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.component.site import UtilityRegistration
+from zope.interface import implements
+
+from zope.app import zapi
+from zope.app.i18n import ZopeMessageIDFactory as _
+from zope.app.component import queryNextSiteManager
+from zope.app.location.interfaces import ILocation
+from zope.app.apidoc.interfaces import IDocumentationModule
+from zope.app.apidoc.utilities import ReadContainerBase, getPythonPath
+
+# Constant used when the utility has no name
+NONAME = '__noname__'
+
+class Utility(object):
+    """Representation of a utility for the API Documentation"""
+    implements(ILocation)
+    
+    def __init__(self, parent, reg):
+        """Initialize Utility object."""
+        self.__parent__ = parent
+        self.__name__ = reg.name or NONAME
+        self.registration = reg
+        self.interface = reg.provided
+        self.component = reg.component
+        self.doc = reg.doc
+
+
+class UtilityInterface(ReadContainerBase):
+    """Representation of an interface a utility provides."""
+    implements(ILocation)
+
+    def __init__(self, parent, name, interface):
+        self.__parent__ = parent
+        self.__name__ = name
+        self.interface = interface
+
+    def get(self, key, default=None):
+        """See zope.app.container.interfaces.IReadContainer"""
+        sm = zapi.getGlobalSiteManager()
+        if key == NONAME:
+            key = ''
+        utils = [Utility(self, reg)
+                 for reg in sm.registrations()
+                 if zapi.isinstance(reg, UtilityRegistration) and \
+                     reg.name == key and reg.provided == self.interface]
+        return utils and utils[0] or default
+
+    def items(self):
+        """See zope.app.container.interfaces.IReadContainer"""
+        sm = zapi.getGlobalSiteManager()
+        items = [(reg.name or NONAME, Utility(self, reg))
+                 for reg in sm.registrations()
+                 if zapi.isinstance(reg, UtilityRegistration) and \
+                     self.interface == reg.provided]
+        items.sort()
+        return items
+
+
+class UtilityModule(ReadContainerBase):
+    """Represent the Documentation of all Interfaces.
+
+    This documentation is implemented using a simple `IReadContainer`. The
+    items of the container are all utility interfaces.
+    """
+    implements(IDocumentationModule)
+
+    # See zope.app.apidoc.interfaces.IDocumentationModule
+    title = _('Utilities')
+
+    # See zope.app.apidoc.interfaces.IDocumentationModule
+    description = _("""
+    Utilities are also nicely registered in a site manager, so that it is easy
+    to create a listing of available utilities. A utility is identified by the
+    providing interface and a name, which can be empty. The menu provides you
+    with a list of interfaces that utilities provide and as sub-items the
+    names of the various implementations.
+
+    Again, the documentation of a utility lists all the attributes/fields and
+    methods the utility provides and provides a link to the implementation.
+    """)
+
+    def get(self, key, default=None):
+        parts = key.split('.')
+        try:
+            mod = __import__('.'.join(parts[:-1]), {}, {}, ('*',))
+        except ImportError:
+            return default
+        else:
+            return UtilityInterface(self, key, getattr(mod, parts[-1], default))
+
+    def items(self):
+        sm = zapi.getSiteManager()
+        ifaces = {}
+        while sm is not None:
+            for reg in sm.registrations():
+                if isinstance(reg, UtilityRegistration):
+                    path = getPythonPath(reg.provided)
+                    ifaces[path] = UtilityInterface(self, path, reg.provided)
+            sm = queryNextSiteManager(sm)
+
+        items = ifaces.items()
+        items.sort(lambda x, y: cmp(x[0].split('.')[-1], y[0].split('.')[-1]))
+        return items
+


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

Modified: Zope3/trunk/src/zope/app/apidoc/version.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/version.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/version.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1 +1 @@
-apidoc 0.1
\ No newline at end of file
+apidoc 0.2

Added: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/README.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/README.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,110 @@
+=============================
+The ZCML Documentation Module
+=============================
+
+This documentation module provides you with a complete reference of all
+directives available on your Zope 3 installation. 
+
+
+`ZCMLModule` class
+------------------
+
+The ZCML module class manages all available ZCML namespaces. Once we
+initialize the module
+
+  >>> from zope.app.apidoc.zcmlmodule import ZCMLModule
+  >>> module = ZCMLModule()
+
+it evaluates all meta directives and creates the namspace list:
+
+  >>> module.get('http://namespaces.zope.org/browser').getFullName()
+  'http://namespaces.zope.org/browser'
+
+You can also access the namespace via its encoded form:
+
+  >>> module.get(
+  ...     'http_co__sl__sl_namespaces.zope.org_sl_browser').getFullName()
+  'http://namespaces.zope.org/browser'
+
+and via its short form:
+
+  >>> module.get('browser').getFullName()
+  'http://namespaces.zope.org/browser'
+
+If the module does not exist, the usual `None` is returned:
+
+  >>> module.get('foo') is None
+  True
+
+You can also list all namespaces:
+
+  >>> names = [n for n, ns in module.items()]
+  >>> 'ALL' in names
+  True
+  >>> 'http_co__sl__sl_namespaces.zope.org_sl_browser' in names
+  True
+  >>> 'http_co__sl__sl_namespaces.zope.org_sl_meta' in names
+  True
+
+
+`Namespace` class
+-----------------
+
+Simple namespace object for the ZCML Documentation Module.
+
+The namespace manages a particular ZCML namespace. The object always
+expects the parent to be a `ZCMLModule` instance. So let's create a namespace:
+
+  >>> module = ZCMLModule()
+  >>> module._makeDocStructure()
+  >>> from zope.app.apidoc.zcmlmodule import Namespace
+  >>> ns = Namespace(ZCMLModule(), 'http://namespaces.zope.org/browser')
+
+We can now get its short name, which is the name without the URL prefix:
+
+  >>> ns.getShortName()
+  'browser'
+
+and its full name in unquoted form:
+
+  >>> ns.getFullName()
+  'http://namespaces.zope.org/browser'
+
+or even quoted:
+
+  >>> ns.getQuotedName()
+  'http_co__sl__sl_namespaces.zope.org_sl_browser'
+
+One can get a directive using the common mapping interface:
+
+  >>> ns.get('pages').__name__
+  'pages'
+
+  >>> ns.get('foo') is None
+  True
+
+  >>> print '\n'.join([name for name, dir in ns.items()][:4])
+  addMenuItem
+  addform
+  addview
+  addwizard
+
+
+`quoteNS(ns)`
+-------------
+
+Quotes a namespace to make it URL-secure.
+
+  >>> from zope.app.apidoc.zcmlmodule import quoteNS
+  >>> quoteNS('http://namespaces.zope.org/browser')
+  'http_co__sl__sl_namespaces.zope.org_sl_browser'
+
+
+`unquoteNS(ns)`
+---------------
+
+Un-quotes a namespace from a URL-secure version.
+
+  >>> from zope.app.apidoc.zcmlmodule import unquoteNS
+  >>> unquoteNS('http_co__sl__sl_namespaces.zope.org_sl_browser')
+  'http://namespaces.zope.org/browser'


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

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/__init__.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/__init__.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -36,64 +36,21 @@
 subdirs = None
 
 def quoteNS(ns):
-    """Quotes a namespace to make it URL-secure.
-
-    Example::
-
-      >>> quoteNS('http://namespaces.zope.org/browser')
-      'http_co__sl__sl_namespaces.zope.org_sl_browser'
-    """
+    """Quotes a namespace to make it URL-secure."""
     ns = ns.replace(':', '_co_')
     ns = ns.replace('/', '_sl_')
     return ns
 
 def unquoteNS(ns):
-    """Un-quotes a namespace from a URL-secure version.
-
-    Example::
-
-      >>> unquoteNS('http_co__sl__sl_namespaces.zope.org_sl_browser')
-      'http://namespaces.zope.org/browser'
-    """
+    """Un-quotes a namespace from a URL-secure version."""
     ns = ns.replace('_sl_', '/')
     ns = ns.replace('_co_', ':')
     return ns    
 
 
 class Namespace(ReadContainerBase):
-    r"""Simple namespace object for the ZCML Documentation Module.
+    """Simple namespace object for the ZCML Documentation Module."""
 
-    The namespace manages a particular ZCML namespace. The object always
-    expects the parent to be a `ZCMLModule` instance.
-
-    Demonstration::
-
-      >>> module = ZCMLModule()
-      >>> module._makeDocStructure()
-      >>> ns = Namespace(ZCMLModule(), 'http://namespaces.zope.org/browser')
-
-      >>> ns.getShortName()
-      'browser'
-
-      >>> ns.getFullName()
-      'http://namespaces.zope.org/browser'
-    
-      >>> ns.getQuotedName()
-      'http_co__sl__sl_namespaces.zope.org_sl_browser'
-
-      >>> ns.get('pages').__name__
-      'pages'
-
-      >>> ns.get('foo') is None
-      True
-
-      >>> print '\n'.join([name for name, dir in ns.items()][:4])
-      addMenuItem
-      addform
-      addview
-      addwizard
-    """
-
     implements(ILocation)
 
     def __init__(self, parent, name):
@@ -152,38 +109,11 @@
     
 
 class ZCMLModule(ReadContainerBase):
-    r"""Represent the Documentation of all Interfaces.
+    r"""Represent the Documentation of all ZCML namespaces.
 
     This documentation is implemented using a simple `IReadContainer`. The
-    items of the container are all the interfaces listed in the closest
-    site manager and above.
+    items of the container."""
 
-    Demonstration::
-
-      >>> module = ZCMLModule()
-
-      >>> module.get('http://namespaces.zope.org/browser').getFullName()
-      'http://namespaces.zope.org/browser'
-
-      >>> module.get(
-      ...     'http_co__sl__sl_namespaces.zope.org_sl_browser').getFullName()
-      'http://namespaces.zope.org/browser'
-
-      >>> module.get('browser').getFullName()
-      'http://namespaces.zope.org/browser'
-
-      >>> module.get('foo') is None
-      True
-
-      >>> names = [ns.getShortName() for n, ns in module.items()]
-      >>> 'browser' in names
-      True
-      >>> 'meta' in names
-      True
-      >>> 'ALL' in names
-      True
-    """
-
     implements(IDocumentationModule)
 
     # See zope.app.apidoc.interfaces.IDocumentationModule

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -29,48 +29,8 @@
 from zope.app.apidoc.utilities import getPythonPath, relativizePath
 
 class Menu(object):
-    """Menu View Helper Class
+    """Menu View Helper Class"""
 
-    Examples::
-
-      >>> from zope.app.tree.node import Node 
-      >>> from zope.app.apidoc.zcmlmodule import Namespace, Directive
-      >>> from zope.app.apidoc.zcmlmodule import ZCMLModule
-      >>> from zope.app.apidoc.tests import Root
-      >>> menu = Menu()
-
-      >>> module = ZCMLModule()
-      >>> module.__parent__ = Root()
-      >>> module.__name__ = 'ZCML'
-
-      Namespace representing directives available in all namespaces.
-
-      >>> ns = Namespace(module, 'ALL')
-      >>> node = Node(ns)
-      >>> menu.getMenuTitle(node)
-      'All Namespaces'
-      >>> menu.getMenuLink(node) is None
-      True
-
-      From now on we use the browser namespace.
-      
-      >>> ns = Namespace(module, 'http://namespaces.zope.org/browser')
-      >>> node = Node(ns)
-      >>> menu.getMenuTitle(node)
-      'browser'
-      >>> menu.getMenuLink(node) is None
-      True
-
-      The namespace has a page directive.
-
-      >>> dir = Directive(ns, 'page', None, None, None, None)
-      >>> node = Node(dir)
-      >>> menu.getMenuTitle(node)
-      'page'
-      >>> menu.getMenuLink(node)
-      './http_co__sl__sl_namespaces.zope.org_sl_browser/page/index.html'
-    """
-
     def getMenuTitle(self, node):
         """Return the title of the node that is displayed in the menu."""
         obj = node.context
@@ -104,80 +64,23 @@
         schema = LocationProxy(schema,
                                self.context,
                                getPythonPath(schema))
-        details = InterfaceDetails()
+        details = InterfaceDetails(schema, self.request)
         details._getFieldName = _getFieldName
-        details.context = schema
-        details.request = self.request
         return details
 
     def getSchema(self):
-        """Return the schema of the directive.
-
-        Examples::
-        
-          >>> from zope.interface import Interface
-          >>> from zope.publisher.browser import TestRequest
-          >>> from tests import getDirective
-          >>> details = DirectiveDetails()
-          >>> details.context = getDirective()
-          >>> details.request = TestRequest()
-
-          >>> class IFoo(Interface):
-          ...     pass
-          >>> details.context.schema = IFoo
-          >>> iface = details.getSchema()
-          >>> if_class = iface.__class__
-          >>> if_class.__module__ + '.' + if_class.__name__
-          'zope.app.apidoc.ifacemodule.browser.InterfaceDetails'
-          >>> iface.context
-          <InterfaceClass zope.app.apidoc.zcmlmodule.browser.IFoo>
-        """
+        """Return the schema of the directive."""
         return self._getInterfaceDetails(self.context.schema)
 
     def getNamespaceName(self):
-        """Return the name of the namespace.
-
-        Examples::
-
-          >>> from tests import getDirective
-          >>> details = DirectiveDetails()
-
-          >>> details.context = getDirective()
-          >>> details.getNamespaceName()
-          'http://namespaces.zope.org/browser'
-
-          >>> details.context.__parent__.__realname__ = 'ALL'
-          >>> details.getNamespaceName()
-          '<i>all namespaces</i>'
-        """
+        """Return the name of the namespace."""
         name = zapi.getParent(self.context).getFullName()
         if name == 'ALL':
             return '<i>all namespaces</i>'
         return name
 
     def getFileInfo(self):
-        """Get the file where the directive was declared.
-
-        Examples::
-        
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from zope.configuration.xmlconfig import ParserInfo
-          >>> from tests import getDirective
-          >>> details = DirectiveDetails()
-          >>> details.context = getDirective()
-
-          >>> details.getFileInfo() is None
-          True
-
-          >>> details.context.info = ParserInfo('foo.zcml', 2, 3)
-          >>> info = details.getFileInfo()
-          >>> pprint(info)
-          [('column', 3),
-           ('ecolumn', 3),
-           ('eline', 2),
-           ('file', 'foo.zcml'),
-           ('line', 2)]
-        """
+        """Get the file where the directive was declared."""
         # ZCML directive `info` objects do not have security declarations, so
         # everything is forbidden by default. We need to remove the security
         # proxies in order to get to the data.  
@@ -191,45 +94,13 @@
         return None
 
     def getInfo(self):
-        """Get the file where the directive was declared.
-
-        Examples::
-        
-          >>> from zope.configuration.xmlconfig import ParserInfo
-          >>> from tests import getDirective
-          >>> details = DirectiveDetails()
-          >>> details.context = getDirective()
-
-          >>> details.getInfo() is None
-          True
-
-          >>> details.context.info = 'info here'
-          >>> details.getInfo()
-          'info here'
-
-          >>> details.context.info = ParserInfo('foo.zcml', 2, 3)
-          >>> details.getInfo() is None
-          True
-        """
+        """Get the file where the directive was declared."""
         if isinstance(self.context.info, (str, unicode)):
             return self.context.info
         return None
 
     def getHandler(self):
-        """Return information about the handler.
-
-        Examples::
-        
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from zope.configuration.xmlconfig import ParserInfo
-          >>> from tests import getDirective
-          >>> details = DirectiveDetails()
-          >>> details.context = getDirective()
-
-          >>> pprint(details.getHandler())
-          [('path', 'zope.app.apidoc.zcmlmodule.tests.foo'),
-           ('url', 'zope/app/apidoc/zcmlmodule/tests/foo')]
-        """
+        """Return information about the handler."""
         if self.context.handler is not None:
             path = getPythonPath(self.context.handler)
             return {'path': path,
@@ -237,41 +108,7 @@
         return None
 
     def getSubdirectives(self):
-        """Create a list of subdirectives.
-
-        Examples::
-        
-          >>> from zope.app.apidoc.tests import pprint
-          >>> from zope.configuration.xmlconfig import ParserInfo
-          >>> from zope.interface import Interface
-          >>> from zope.publisher.browser import TestRequest
-          >>> from tests import getDirective
-          >>> details = DirectiveDetails()
-          >>> details.context = getDirective()
-          >>> details.request = TestRequest()
-
-          >>> class IFoo(Interface):
-          ...     pass
-
-          >>> def handler():
-          ...     pass
-
-          >>> details.getSubdirectives()
-          []
-
-          >>> details.context.subdirs = (
-          ...     ('browser', 'foo', IFoo, handler, 'info'),)
-          >>> info = details.getSubdirectives()[0]
-          >>> info['schema'] = info['schema'].__module__ + '.InterfaceDetails'
-          >>> pprint(info)
-          [('handler',
-            [('path', 'zope.app.apidoc.zcmlmodule.browser.handler'),
-             ('url', 'zope/app/apidoc/zcmlmodule/browser/handler')]),
-           ('info', 'info'),
-           ('name', 'foo'),
-           ('namespace', 'browser'),
-           ('schema', 'zope.app.apidoc.ifacemodule.browser.InterfaceDetails')]
-        """
+        """Create a list of subdirectives."""
         dirs = []
         for ns, name, schema, handler, info in self.context.subdirs:
             details = self._getInterfaceDetails(schema)

Added: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.txt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.txt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -0,0 +1,218 @@
+======================================
+Module Menu and ZCML Directive Details
+======================================
+
+
+`Menu` class
+------------
+
+Let's start out by creating a menu. First we isntantiate the class:
+
+  >>> from zope.app.apidoc.zcmlmodule.browser import Menu
+  >>> menu = Menu()
+
+then we create a ZCML module instance:
+
+  >>> from zope.app.apidoc.zcmlmodule import ZCMLModule
+  >>> from zope.app.apidoc.tests import Root
+  >>> module = ZCMLModule()
+  >>> module.__parent__ = Root()
+  >>> module.__name__ = 'ZCML'
+
+Now we create a namespace representing directives available in all namespaces
+
+  >>> from zope.app.apidoc.zcmlmodule import Namespace
+  >>> ns = Namespace(module, 'ALL')
+
+and generate a tree node :
+
+  >>> from zope.app.tree.node import Node 
+  >>> node = Node(ns)
+
+We can now ask the menu for the title of the namespace
+
+  >>> menu.getMenuTitle(node)
+  'All Namespaces'
+
+and the link to the namespace overview.
+
+  >>> menu.getMenuLink(node) is None
+  True
+
+Since the 'ALL' namespace is not that useful, let's create a namespace
+instance for the browser namespace:
+  
+  >>> ns = Namespace(module, 'http://namespaces.zope.org/browser')
+  >>> node = Node(ns)
+
+And again we can get its title and menu link:
+
+  >>> menu.getMenuTitle(node)
+  'browser'
+  >>> menu.getMenuLink(node) is None
+  True
+
+Now we add the `page` directive to the browser namespace: 
+
+  >>> from zope.app.apidoc.zcmlmodule import Directive
+  >>> dir = Directive(ns, 'page', None, None, None, None)
+  >>> node = Node(dir)
+
+And we can get its menu title and link.
+
+  >>> menu.getMenuTitle(node)
+  'page'
+  >>> menu.getMenuLink(node)
+  './http_co__sl__sl_namespaces.zope.org_sl_browser/page/index.html'
+
+Note that the directive's namespace URL is encoded, so it can be used in a
+URL.
+
+
+`DirectiveDetails` class
+------------------------
+
+A browser view class that provides support for the ZCML directive overview.
+
+Let's create a directive that we can use as context for the details:
+
+  >>> from zope.interface import Interface, Attribute
+  >>> class IFoo(Interface):
+  ...     class_ = Attribute('class_')
+
+  >>> def foo(): 
+  ...     pass
+
+  >>> directive = Directive(ns, 'page', IFoo, foo, None, ())
+
+Now we can isntantiate the view:
+
+  >>> from zope.publisher.browser import TestRequest
+  >>> from zope.app.apidoc.zcmlmodule.browser import DirectiveDetails
+
+  >>> details = DirectiveDetails()
+  >>> details.context = directive
+  >>> details.request = TestRequest()
+
+We are now ready to see what the details class has to offer.
+
+
+`getSchema()`
+~~~~~~~~~~~~~
+
+Returns the interface details class for the schema.
+
+  >>> iface_details = details.getSchema()
+
+  >>> iface_details #doctest:+ELLIPSIS
+  <zope.app.apidoc.ifacemodule.browser.InterfaceDetails object at ...>
+
+  >>> iface_details.context
+  <InterfaceClass __builtin__.IFoo>
+
+The `_getFieldName()` method of the interface details has been overridden to
+neglect trailing underscores in the field name. This is necessary, since
+Python keywords cannot be used as field names:
+
+  >>> iface_details._getFieldName(IFoo['class_'])
+  'class'
+
+
+`getNamespaceName()`
+~~~~~~~~~~~~~~~~~~~~
+
+Return the name of the namespace.
+
+  >>> details.getNamespaceName()
+  'http://namespaces.zope.org/browser'
+
+If the directive is in the 'ALL' namespace, a special string is returned:
+
+  >>> details2 = DirectiveDetails()
+  >>> ns2 = Namespace(module, 'ALL')
+  >>> details2.context = Directive(ns2, 'include', None, None, None, None)
+
+  >>> details2.getNamespaceName()
+  '<i>all namespaces</i>'
+
+
+`getFileInfo()`
+~~~~~~~~~~~~~~~
+
+Get the file where the directive was declared. If the info attribute is not
+set, return `None`:
+
+  >>> details.getFileInfo() is None
+  True
+
+If the info attribute is a parser info, then return the details:
+
+  >>> from zope.configuration.xmlconfig import ParserInfo
+  >>> details.context.info = ParserInfo('foo.zcml', 2, 3)
+  >>> info = details.getFileInfo()
+  >>> pprint(info)
+  {'column': 3,
+   'ecolumn': 3,
+   'eline': 2,
+   'file': 'foo.zcml',
+   'line': 2}
+
+If the info is a string, `None` should be returned again:
+
+  >>> details.context.info = 'info here'
+  >>> details.getFileInfo() is None
+  True
+
+
+`getInfo()`
+~~~~~~~~~~~
+
+Get the configuration information string of the directive:
+
+  >>> details.context.info = 'info here'
+  >>> details.getInfo()
+  'info here'
+
+Return `None`, if the info attribute is a parser info:
+
+  >>> details.context.info = ParserInfo('foo.zcml', 2, 3)
+  >>> details.getInfo() is None
+  True
+
+
+`getHandler()`
+~~~~~~~~~~~~~~
+
+Return information about the directive handler object.
+
+  >>> pprint(details.getHandler())
+  {'path': 'None.foo',
+   'url': 'None/foo'}
+
+
+`getSubdirectives()`
+~~~~~~~~~~~~~~~~~~~~
+
+Create a list of subdirectives. Currently, we have not specifiedany
+subdirectives
+
+  >>> details.getSubdirectives()
+  []
+
+but if we add one
+
+  >>> def handler():
+  ...     pass
+
+  >>> details.context.subdirs = (
+  ...     ('browser', 'foo', IFoo, handler, 'info'),)
+
+the result becomes more interesting:
+
+  >>> pprint(details.getSubdirectives()) #doctest:+ELLIPSIS
+  [{'handler': {'path': 'None.handler',
+                'url': 'None/handler'},
+    'info': 'info',
+    'name': 'foo',
+    'namespace': 'browser',
+    'schema': <zope.app.apidoc.ifacemodule.browser.InterfaceDetails ...>}]
\ No newline at end of file


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

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -1,6 +1,7 @@
 <html metal:use-macro="views/apidoc_macros/details">
 <body metal:fill-slot="contents"
-      tal:define="schema view/getSchema">
+      tal:define="schema view/getSchema;
+                  rootURL string:../../..">
 
   <h1 class="details-header">
     <span tal:replace="context/zope:name" /> 
@@ -37,7 +38,7 @@
     <i i18n:translate="">Handler:</i>
     <a href=""
        tal:attributes="href 
-           string:../../../Class/${handler/url}/index.html"
+           string:../../../Code/${handler/url}/index.html"
        tal:content="handler/path">
     </a>
   </div>
@@ -58,18 +59,7 @@
       tal:condition="fields">
   
     <li tal:repeat="field fields">
-      <b tal:attributes="class field/required_css">
-        <code tal:content="field/name">field</code>
-        <span tal:condition="field/required">*</span>
-      </b>
-      - <a href=""
-           tal:attributes="href 
-               string:../../../Class/${field/class/path}/index.html">
-          <code tal:content="field/class/name">Field</code></a>
-      (<span i18n:translate="">default</span> =
-            <code tal:content="field/default" />)<br />
-      <div tal:content="structure field/title" class="field-title">title</div>
-      <span tal:content="structure field/description">field desc</span>      
+      <metal:block use-macro="context/@@interface_macros/field" />
     </li>
 
   </ul>
@@ -80,7 +70,7 @@
 
   </div>
 
-  <tal:omit-tag 
+  <tal:block
       define="dir view/getSubdirectives"
       condition="dir">
 
@@ -93,10 +83,8 @@
     directive
   </h3>
 
-  <div>
-    <div class="documentation" tal:content="structure dir/schema/getDoc">
-      Here is the doc string
-    </div>
+  <div class="documentation" tal:content="structure dir/schema/getDoc">
+    Here is the doc string
   </div>
 
   <br />
@@ -106,7 +94,7 @@
     <i i18n:translate="">Handler:</i>
     <a href=""
        tal:attributes="href 
-           string:../../../Class/${dir/handler/url}/index.html"
+           string:../../../Code/${dir/handler/url}/index.html"
        tal:content="dir/handler/path">
     </a>
   </div>
@@ -115,42 +103,31 @@
     <a href=""
        tal:attributes="href 
            string:../../../Interface/${dir/schema/getId}/apiindex.html">
-      <h4 tal:content="dir/schema/getId">zope.fields.Schema</h4>
+      <h3 tal:content="dir/schema/getId">zope.fields.Schema</h3>
     </a>
   </div>
 
   <div class="indent"
       tal:define="fields dir/schema/getFields">
 
-  <ul class="attr-list" 
-      tal:condition="fields">
-  
-    <li tal:repeat="field fields">
-      <b tal:attributes="class field/required_css">
-        <code tal:content="field/name">field</code>
-        <span tal:condition="field/required">*</span>
-      </b>
-      - <a href=""
-           tal:attributes="href 
-               string:../../../Interface/${field/iface/id}/apiindex.html">
-          <code tal:content="field/iface/name">IField</code></a>
-      (<span i18n:translate="">default</span> =
-            <code tal:content="field/default" />)<br />
-      <div tal:content="structure field/title" class="field-title">title</div>
-      <span tal:content="structure field/description">field desc</span>
-    </li>
+    <ul class="attr-list" 
+        tal:condition="fields">
+    
+      <li tal:repeat="field fields">
+        <metal:block use-macro="context/@@interface_macros/field" />
+      </li>
+    
+    </ul>
+    
+    <p tal:condition="not: fields">
+      <em i18n:translate="">There are no fields specified.</em>
+    </p>
 
-  </ul>
-
-  <p tal:condition="not: fields">
-    <em i18n:translate="">There are no fields specified.</em>
-  </p>
-
   </div>
 
   </div>
 
-  </tal:omit-tag>
+  </tal:block>
 
   <p>
     <em><b class="required">*</b> =

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/tests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/tests.py	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/tests.py	2005-02-23 22:22:48 UTC (rev 29269)
@@ -17,7 +17,7 @@
 """
 import os
 import unittest
-from zope.testing.doctestunit import DocTestSuite
+from zope.testing import doctest, doctestunit
 from zope.app.testing import placelesssetup, ztapi
 from zope.app.apidoc.tests import Root
 
@@ -62,11 +62,15 @@
 
 def test_suite():
     return unittest.TestSuite((
-        DocTestSuite('zope.app.apidoc.zcmlmodule',
-                     setUp=setUp, tearDown=tearDown),
-        DocTestSuite('zope.app.apidoc.zcmlmodule.browser',
-                     setUp=setUp, tearDown=tearDown),
+        doctest.DocFileSuite('README.txt',
+                             setUp=setUp, tearDown=tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
+        doctest.DocFileSuite('browser.txt',
+                             setUp=setUp, tearDown=tearDown,
+                             globs={'pprint': doctestunit.pprint},
+                             optionflags=doctest.NORMALIZE_WHITESPACE),
         ))
 
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(default='test_suite')

Modified: Zope3/trunk/src/zope/app/introspector/introspector.pt
===================================================================
--- Zope3/trunk/src/zope/app/introspector/introspector.pt	2005-02-23 22:17:22 UTC (rev 29268)
+++ Zope3/trunk/src/zope/app/introspector/introspector.pt	2005-02-23 22:22:48 UTC (rev 29269)
@@ -113,7 +113,7 @@
           <td><a tal:define="path python: (introspector.getModule()
                 + '.' + introspector.getClass()).replace('.', '/')"
                 tal:attributes="href
-                string: /++apidoc++/Class/${path}/index.html"
+                string: /++apidoc++/Code/${path}/index.html"
                 href="">
             <span tal:replace="introspector/getModule">Module</span
             >.<span tal:replace="introspector/getClass">Name</span></a></td>
@@ -129,7 +129,7 @@
               <a tal:define="path
                 python: path('repeat/base/item').replace('.', '/')"
                 tal:attributes="href
-                string: /++apidoc++/Class/${path}/index.html"
+                string: /++apidoc++/Code/${path}/index.html"
                 href=""><span tal:replace="repeat/base/item" /></a>
             </div>
           </td>



More information about the Zope3-Checkins mailing list