[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/apidoc/ - Created APIDOC skin, since I wanted to override existing views.

Stephan Richter srichter at cosmos.phy.tufts.edu
Mon Oct 31 00:16:33 EST 2005


Log message for revision 39760:
  - Created APIDOC skin, since I wanted to override existing views.
  
  - Created custom not found view.
  
  - Fixed up apidoc code to cause less failures during static apidoc 
    generation.
  
  - Improved reporting and debugging capabilities in the static apidoc 
    generator.
  
  - The static apidoc generator now will store error pages as well.
  
  

Changed:
  U   Zope3/trunk/src/zope/app/apidoc/apidoc.py
  U   Zope3/trunk/src/zope/app/apidoc/bookmodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/bookmodule/metadirectives.py
  A   Zope3/trunk/src/zope/app/apidoc/browser/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/browser/ftests.py
  A   Zope3/trunk/src/zope/app/apidoc/browser/notfound.pt
  A   Zope3/trunk/src/zope/app/apidoc/browser/skin.py
  U   Zope3/trunk/src/zope/app/apidoc/classregistry.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/module.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/text.py
  U   Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py
  U   Zope3/trunk/src/zope/app/apidoc/component.py
  U   Zope3/trunk/src/zope/app/apidoc/component.txt
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py
  U   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/interface.py
  U   Zope3/trunk/src/zope/app/apidoc/interface.txt
  U   Zope3/trunk/src/zope/app/apidoc/static.py
  U   Zope3/trunk/src/zope/app/apidoc/typemodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/utilities.py
  U   Zope3/trunk/src/zope/app/apidoc/utilities.txt
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/configure.zcml

-=-
Modified: Zope3/trunk/src/zope/app/apidoc/apidoc.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/apidoc.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/apidoc.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -13,7 +13,7 @@
 ##############################################################################
 """Zope 3 API Documentation
 
-$Id: __init__.py 26777 2004-07-27 08:12:32Z hdima $
+$Id$
 """
 __docformat__ = 'restructuredtext'
 
@@ -22,6 +22,7 @@
 from zope.app import zapi
 from zope.app.location import locate
 from zope.app.location.interfaces import ILocation
+from zope.app.publisher.browser import applySkin
 
 from interfaces import IDocumentationModule
 from utilities import ReadContainerBase
@@ -38,7 +39,7 @@
     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)
@@ -55,17 +56,19 @@
             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):
+        if request:
+            from zope.app.apidoc.browser.skin import APIDOC
+            applySkin(request, APIDOC)
         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)
-    

Modified: Zope3/trunk/src/zope/app/apidoc/bookmodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/bookmodule/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/bookmodule/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -23,14 +23,16 @@
       class=".browser.Menu"
       name="menu.html"
       template="menu.pt"
+      layer="..browser.skin.apidoc"
       />
-  
+
     <browser:page
       for=".book.IBookModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.Menu"
       name="staticmenu.html"
       template="static_menu.pt"
+      layer="..browser.skin.apidoc"
       />
 
   <browser:page
@@ -39,6 +41,7 @@
       class="zope.app.onlinehelp.browser.OnlineHelpTopicView"
       name="show.html"
       template="chapter.pt"
+      layer="..browser.skin.apidoc"
       />
 
   <include file="book.zcml" />

Modified: Zope3/trunk/src/zope/app/apidoc/bookmodule/metadirectives.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/bookmodule/metadirectives.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/bookmodule/metadirectives.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -47,10 +47,9 @@
 
     resources = Tokens(
         title=u"A list of resources.",
-        description=u"""\
-        A list of resources which shall be user for the chapter.
-        The resources must be located in the same directory as
-        the chapter.
+        description=u"""
+        A list of resources which shall be user for the chapter. The
+        resources must be located in the same directory as the chapter.
         """,
         value_type=TextLine(),
         required=False

Added: Zope3/trunk/src/zope/app/apidoc/browser/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/README.txt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/browser/README.txt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -0,0 +1,44 @@
+=====================
+Generic API Doc Views
+=====================
+
+Get a browser started:
+
+  >>> from zope.testbrowser import Browser
+  >>> browser = Browser()
+  >>> browser.addHeader('Authorization', 'Basic mgr:mgrpw')
+
+
+Not Found View
+--------------
+
+The `APIDOC` skin defines a custom not found view, since it fits the look and
+feel better and does not have all the O-wrap clutter:
+
+  >>> browser.open('http://localhost/++apidoc++/non-existent/')
+  Traceback (most recent call last):
+  ...
+  HTTPError: HTTP Error 404: Not Found
+
+  >>> from urllib2 import HTTPError
+  >>> try:
+  ...     browser.open('http://localhost/++apidoc++/non-existent/')
+  ... except HTTPError, error:
+  ...     pass
+
+  >>> print error.read()
+  Status: 404 Not Found
+  Content-Length: ...
+  Content-Type: text/html;charset=utf-8
+  ...
+  <h1 class="details-header">
+    Page Not Found
+  </h1>
+  <BLANKLINE>
+  <p>
+    While broken links occur occassionally, they are considered bugs. Please
+    report any broken link to
+    <a href="mailto:zope3-dev at zope.org">zope3-dev at zope.org</a>.
+  </p>
+  ...
+


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

Modified: Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/browser/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -2,14 +2,26 @@
   xmlns="http://namespaces.zope.org/browser"
   i18n_domain="zope">
 
-  <resource name="apidoc.css" file="apidoc.css" />
+  <layer
+      name="apidoc"
+      interface=".skin.apidoc" />
 
+  <skin
+      name="APIDOC"
+      interface=".skin.APIDOC" />
+
+  <resource
+      name="apidoc.css"
+      file="apidoc.css"
+      />
+
   <page
       for="*"
       name="apidoc_macros"
       permission="zope.View"
       class=".macros.APIDocumentationMacros"
       allowed_interface="zope.interface.common.mapping.IItemMapping"
+      layer=".skin.apidoc"
       />
 
   <page
@@ -17,6 +29,7 @@
       name="menu_macros"
       permission="zope.View"
       template="menu_macros.pt"
+      layer=".skin.apidoc"
       />
 
   <page
@@ -24,6 +37,7 @@
       name="static_menu_macros"
       permission="zope.View"
       template="static_menu_macros.pt"
+      layer=".skin.apidoc"
       />
 
   <page
@@ -31,6 +45,7 @@
       name="details_macros"
       permission="zope.View"
       template="details_macros.pt"
+      layer=".skin.apidoc"
       />
 
   <resource
@@ -51,7 +66,8 @@
   <pages
     for="zope.app.apidoc.apidoc.APIDocumentation"
     class=".apidoc.APIDocumentationView"
-    permission="zope.app.apidoc.UseAPIDoc">
+    permission="zope.app.apidoc.UseAPIDoc"
+    layer=".skin.apidoc">
 
     <page
         name="index.html"
@@ -76,7 +92,8 @@
   <pages
     for="zope.app.apidoc.apidoc.APIDocumentation"
     class=".apidoc.APIDocumentationView"
-    permission="zope.app.apidoc.UseAPIDoc">
+    permission="zope.app.apidoc.UseAPIDoc"
+    layer=".skin.apidoc">
 
     <page
         name="static.html"
@@ -96,7 +113,17 @@
 
   </pages>
 
+  <!-- Error Views -->
 
+  <page
+      for="zope.publisher.interfaces.INotFound"
+      name="index.html"
+      permission="zope.Public"
+      template="notfound.pt"
+      class="zope.app.exception.browser.notfound.NotFound"
+      layer=".skin.apidoc"
+      />
+
   <!-- Preference Views -->
 
   <page
@@ -105,6 +132,7 @@
       class=".preference.APIDocPreferencesTree"
       permission="zope.View"
       attribute="apidocTree"
+      layer=".skin.apidoc"
       />
 
   <page
@@ -112,6 +140,7 @@
       permission="zope.Public"
       name="apidocMenu.html"
       template="prefmenu.pt"
+      layer=".skin.apidoc"
       />
 
   <page
@@ -120,6 +149,7 @@
       name="apidocIndex.html"
       template="prefIndex.pt"
       class="zope.app.preference.browser.EditPreferenceGroup"
+      layer=".skin.apidoc"
       />
 
 </configure>

Modified: Zope3/trunk/src/zope/app/apidoc/browser/ftests.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/ftests.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/browser/ftests.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -16,7 +16,9 @@
 $Id$
 """
 import unittest
+from zope.testing import doctest
 from zope.app.testing.functional import BrowserTestCase
+from zope.app.testing.functional import FunctionalDocFileSuite
 
 class APIDocTests(BrowserTestCase):
     """Just a couple of tests ensuring that the templates render."""
@@ -63,6 +65,9 @@
 def test_suite():
     return unittest.TestSuite((
         unittest.makeSuite(APIDocTests),
+        FunctionalDocFileSuite(
+            "README.txt",
+            optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE),
         ))
 
 if __name__ == '__main__':

Added: Zope3/trunk/src/zope/app/apidoc/browser/notfound.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/notfound.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/browser/notfound.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -0,0 +1,15 @@
+<html metal:use-macro="context/@@apidoc_macros/details" i18n:domain="zope">
+<body metal:fill-slot="contents">
+
+  <h1 class="details-header" >
+    Page Not Found
+  </h1>
+
+  <p>
+    While broken links occur occassionally, they are considered bugs. Please
+    report any broken link to
+    <a href="mailto:zope3-dev at zope.org">zope3-dev at zope.org</a>.
+ </p>
+
+</body>
+</html>


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

Added: Zope3/trunk/src/zope/app/apidoc/browser/skin.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/browser/skin.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/browser/skin.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""`APIdoc` skin.
+
+$Id$
+"""
+__docformat__ = "reStructuredText"
+
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.interfaces.browser import IDefaultBrowserLayer
+
+class apidoc(IBrowserRequest):
+    """The `apidoc` layer."""
+
+class APIDOC(apidoc, IDefaultBrowserLayer):
+    """The `APIDOC` skin."""
+


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

Modified: Zope3/trunk/src/zope/app/apidoc/classregistry.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classregistry.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/classregistry.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -20,7 +20,8 @@
 __import_unknown_modules__ = False
 
 # List of modules that should never be imported.
-IGNORE_MODULES = []
+# TODO: List hard-coded for now.
+IGNORE_MODULES = ['twisted']
 
 import sys
 

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/README.txt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -7,7 +7,7 @@
   >>> from zope.app.apidoc import codemodule
 
 provides systematic and autogenerated documentation about the content of your
-Zope 3 related Python packages. The code module can be created like this: 
+Zope 3 related Python packages. The code module can be created like this:
 
   >>> cm = codemodule.codemodule.CodeModule()
 

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/README.txt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -22,7 +22,7 @@
 The module details are easily created, since we can just use the traversal
 process to get a module documentation object:
 
-  >>> details = browser.module.ModuleDetails()
+  >>> details = browser.module.ModuleDetails(None, None)
   >>> details.context = zapi.traverse(cm,
   ...     'zope/app/apidoc/codemodule/codemodule')
   >>> from zope.publisher.browser import TestRequest
@@ -51,6 +51,7 @@
     'istextfile': False,
     'iszcmlfile': False,
     'name': 'CodeModule',
+    'path': 'zope.app.apidoc.codemodule.class_.CodeModule',
     'url': 'http://127.0.0.1/zope/app/apidoc/codemodule/codemodule/CodeModule'}]
 
 `getBreadCrumbs()`
@@ -139,10 +140,14 @@
 Get all implemented interfaces (as paths) of this class.
 
   >>> pprint(details.getInterfaces())
-  ['zope.app.apidoc.interfaces.IDocumentationModule',
-   'zope.app.location.interfaces.ILocation',
-   'zope.app.apidoc.codemodule.interfaces.IModuleDocumentation',
-   'zope.app.container.interfaces.IReadContainer']
+  [{'path': 'zope.app.apidoc.interfaces.IDocumentationModule',
+    'url': 'zope.app.apidoc.interfaces.IDocumentationModule'},
+   {'path': 'zope.app.location.interfaces.ILocation',
+    'url': 'zope.app.location.interfaces.ILocation'},
+   {'path': 'zope.app.apidoc.codemodule.interfaces.IModuleDocumentation',
+    'url': 'zope.app.apidoc.codemodule.interfaces.IModuleDocumentation'},
+   {'path': 'zope.app.container.interfaces.IReadContainer',
+    'url': 'zope.app.container.interfaces.IReadContainer'}]
 
 `getAttributes()`
 ~~~~~~~~~~~~~~~~~
@@ -150,7 +155,8 @@
 Get all attributes of this class.
 
   >>> pprint(details.getAttributes()[1])
-  {'interface': 'zope.app.apidoc.interfaces.IDocumentationModule',
+  {'interface': {'path': 'zope.app.apidoc.interfaces.IDocumentationModule',
+                 'url': 'zope.app.apidoc.interfaces.IDocumentationModule'},
    'name': 'title',
    'read_perm': None,
    'type': 'Message',
@@ -170,7 +176,8 @@
     'signature': '()',
     'write_perm': None},
    {'doc': u'',
-    'interface': 'zope.interface.common.mapping.IEnumerableMapping',
+    'interface': {'path': 'zope.interface.common.mapping.IEnumerableMapping',
+                  'url': 'zope.interface.common.mapping.IEnumerableMapping'},
     'name': 'values',
     'read_perm': None,
     'signature': '()',

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -16,6 +16,8 @@
 $Id: browser.py 29143 2005-02-14 22:43:16Z srichter $
 """
 __docformat__ = 'restructuredtext'
+
+import inspect
 import types
 from zope.proxy import removeAllProxies
 from zope.security.proxy import removeSecurityProxy
@@ -34,6 +36,13 @@
     path = getPythonPath(type)
     return isReferencable(path) and path.replace('.', '/') or None
 
+def getInterfaceInfo(iface):
+    if iface is None:
+        return None
+    path = getPythonPath(iface)
+    return {'path': path,
+            'url': isReferencable(path) and path or None}
+
 class ClassDetails(object):
     """Represents the details of the class."""
 
@@ -48,6 +57,7 @@
         entries.sort(lambda x, y: cmp(x['path'], y['path']))
         return entries
 
+
     def _listClasses(self, classes):
         """Prepare a list of classes for presentation."""
         info = []
@@ -59,13 +69,14 @@
             # accessing __name__ and __module__.
             unwrapped_cls = removeAllProxies(cls)
             path = getPythonPath(unwrapped_cls)
+            url = None
             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
+                pass
             info.append({'path': path or None, 'url': url})
         return info
 
@@ -78,7 +89,8 @@
 
     def getInterfaces(self):
         """Get all implemented interfaces (as paths) of this class."""
-        return map(getPythonPath, self.context.getInterfaces())
+        return [getInterfaceInfo(iface)
+                for iface in self.context.getInterfaces()]
 
 
     def getAttributes(self):
@@ -95,7 +107,7 @@
                      'value': `attr`,
                      'type': type(attr).__name__,
                      'type_link': getTypeLink(type(attr)),
-                     'interface': getPythonPath(iface)}
+                     'interface': getInterfaceInfo(iface)}
             entry.update(getPermissionIds(name, klass.getSecurityChecker()))
             attrs.append(entry)
         return attrs
@@ -114,16 +126,17 @@
             entry = {'name': name,
                      'signature': "(...)",
                      'doc': renderText(attr.__doc__ or '',
-                                       zapi.getParent(self.context).getPath()),
-                     'interface': getPythonPath(iface)}
+                                       inspect.getmodule(attr)),
+                     'interface': getInterfaceInfo(iface)}
             entry.update(getPermissionIds(name, klass.getSecurityChecker()))
             methods.append(entry)
+
         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)}
+                                       inspect.getmodule(attr)),
+                     'interface': getInterfaceInfo(iface)}
             entry.update(getPermissionIds(name, klass.getSecurityChecker()))
             methods.append(entry)
         return methods

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/class_index.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -26,10 +26,8 @@
             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>
+        <span tal:replace="base/path"
+              tal:condition="not: base/url"/>
       </li>
     </ul>
 
@@ -48,8 +46,11 @@
       <li tal:repeat="iface ifaces">
         <a href=""
            tal:attributes="href
-               string:${view/getBaseURL}/Interface/$iface/index.html"
-           tal:content="iface" />
+               string:${view/getBaseURL}/Interface/${iface/url}/index.html"
+           tal:content="iface/path"
+           tal:condition="iface/url"/>
+        <span tal:replace="iface/path"
+              tal:condition="not: iface/url"/>
       </li>
     </ul>
 
@@ -87,8 +88,12 @@
         <i i18n:translate="">Interface:</i>
         <a href=""
            tal:attributes="href
-               string:${view/getBaseURL}/Interface/${attr/interface}/index.html"
-           tal:content="attr/interface">Iface</a><br />
+      string:${view/getBaseURL}/Interface/${attr/interface/url}/index.html"
+           tal:content="attr/interface/path"
+           tal:condition="attr/interface/url" />
+        <span tal:replace="attr/interface/path"
+              tal:condition="not: attr/interface/url" />
+      <br />
       </span>
       <span class="small"
           tal:condition="python: attr['read_perm'] and attr['write_perm']">
@@ -128,8 +133,12 @@
         <i i18n:translate="">Interface:</i>
         <a href=""
            tal:attributes="href
-             string:${view/getBaseURL}/Interface/${method/interface}/index.html"
-           tal:content="method/interface">Iface</a><br/>
+      string:${view/getBaseURL}/Interface/${method/interface/url}/index.html"
+           tal:content="method/interface/path"
+           tal:condition="method/interface/url" />
+        <span tal:replace="method/interface/path"
+              tal:condition="not: method/interface/url" />
+      <br/>
       </span>
 
       <span class="small"

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -7,56 +7,64 @@
       permission="zope.app.apidoc.UseAPIDoc"
       class=".menu.Menu"
       name="menu.html"
-      template="menu.pt" />
+      template="menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <page
       for="..codemodule.CodeModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".menu.Menu"
       name="staticmenu.html"
-      template="static_menu.pt" />
+      template="static_menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <page
       for="..interfaces.IModuleDocumentation"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".module.ModuleDetails"
       name="index.html"
-      template="module_index.pt" />
+      template="module_index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <page
       for="..interfaces.IClassDocumentation"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".class_.ClassDetails"
       name="index.html"
-      template="class_index.pt" />
+      template="class_index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <page
       for="..interfaces.IFunctionDocumentation"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".function.FunctionDetails"
       name="index.html"
-      template="function_index.pt" />
+      template="function_index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <page
       for="..text.TextFile"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".text.TextFileDetails"
       name="index.html"
-      template="textfile_index.pt" />
+      template="textfile_index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <!-- ZCML File -->
   <page
       for="..interfaces.IZCMLFile"
       name="index.html"
       template="zcmlfile_index.pt"
-      permission="zope.View"/>
+      permission="zope.View"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <page
       name="display"
       for="..interfaces.IDirective"
       template="directive.pt"
       class=".zcml.DirectiveDetails"
-      permission="zope.ManageContent"/>
+      permission="zope.ManageContent"
+      layer="zope.app.apidoc.browser.skin.apidoc" />
 
   <include file="introspector.zcml" />
 

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -1,7 +1,7 @@
 <html metal:use-macro="context/@@standard_macros/view">
 <head>
   <metal:block fill-slot="extrahead">
-    <link type="text/css" rel="stylesheet" href="introspector.css"
+    <link type="text/css" rel="stylesheet" href="apidoc.css"
           tal:attributes="href context/++resource++apidoc.css" />
   </metal:block>
 </head>
@@ -63,8 +63,8 @@
       <li tal:repeat="iface ifaces">
         <a href=""
            tal:attributes="href
-             string:${view/getBaseURL}/Interface/$iface/index.html"
-           tal:content="iface" />
+             string:${view/getBaseURL}/Interface/${iface/url}/index.html"
+           tal:content="iface/path" />
       </li>
     </ul>
 
@@ -83,7 +83,6 @@
             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>
@@ -129,7 +128,7 @@
         <i i18n:translate="">Interface:</i>
         <a href=""
            tal:attributes="href
-               string:${view/getBaseURL}/Interface/${attr/interface}/index.html"
+         string:${view/getBaseURL}/Interface/${attr/interface}/index.html"
            tal:content="attr/interface">Iface</a><br />
       </span>
       <span class="small"

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/introspector.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -22,7 +22,7 @@
 
 import zope.interface
 import zope.security.proxy
-from zope.interface import directlyProvidedBy
+from zope.interface import directlyProvidedBy, directlyProvides
 from zope.app import zapi, apidoc, annotation
 from zope.app.location import location
 from zope.app.publisher.browser import BrowserView
@@ -100,10 +100,16 @@
         path = apidoc.utilities.getPythonPath(
             context.__class__).replace('.', '/')
 
+        # the ++apidoc++ namespace overrides the skin, so make sure we can get
+        # it back.
+        direct = list(directlyProvidedBy(request))
+
         self.klassView = zapi.traverse(
             TraversalRoot(),
             '/++apidoc++/Code/%s/@@index.html' %path, request=request)
 
+        directlyProvides(request, direct)
+
     def parent(self):
         return zapi.getParent(self.context)
 

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -22,18 +22,33 @@
 
 from zope.app import zapi
 from zope.app.i18n import ZopeMessageFactory as _
+from zope.app.publisher.browser import BrowserView
 
-from zope.app.apidoc.utilities import renderText, columnize
+from zope.app.apidoc.apidoc import APIDocumentation
+from zope.app.apidoc.utilities import getPythonPath, 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 ZCMLFile
 
+def findAPIDocumentationRoot(obj, request):
+    if zapi.isinstance(obj, APIDocumentation):
+        return zapi.absoluteURL(obj, request)
+    return findAPIDocumentationRoot(zapi.getParent(obj), request)
 
-class ModuleDetails(object):
+
+class ModuleDetails(BrowserView):
     """Represents the details of the module."""
 
+    def __init__(self, context, request):
+        super(ModuleDetails, self).__init__(context, request)
+        try:
+            self.apidocRoot = findAPIDocumentationRoot(context, request)
+        except TypeError:
+            # Probably context without location; it's a test
+            self.apidocRoot = ''
+
     def getDoc(self):
         """Get the doc string of the module STX formatted."""
         text = self.context.getDocString()
@@ -47,6 +62,8 @@
     def getEntries(self, columns=True):
         """Return info objects for all modules and classes in this module."""
         entries = [{'name': name,
+                    # only for interfaces; should be done differently somewhen
+                    'path': getPythonPath(removeAllProxies(obj)),
                     'url': zapi.absoluteURL(obj, self.request),
                     'ismodule': zapi.isinstance(obj, Module),
                     'isinterface': zapi.isinstance(

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/module_index.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -28,27 +28,28 @@
     <li tal:repeat="entry column">
       <a href=""
          tal:condition="entry/ismodule"
-         tal:attributes="href string:./${entry/name}/index.html"
+         tal:attributes="href string:${entry/url}/index.html"
          tal:content="entry/name" />
       <a href=""
          tal:condition="entry/isinterface"
-         tal:attributes="href string:./${entry/name}/index.html"
+         tal:attributes="
+             href string:${view/apidocRoot}/Interface/${entry/path}/index.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:attributes="href string:${entry/url}/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:attributes="href string:${entry/url}/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:attributes="href string:${entry/url}/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:attributes="href string:${entry/url}/index.html"
          tal:content="structure string:<i>${entry/name}</i>" />
     </li>
   </ul></td>

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/textfile_index.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -1,4 +1,10 @@
 <html metal:use-macro="views/apidoc_macros/details">
+<head>
+  <base href=""
+        metal:fill-slot="headers"
+        tal:attributes="href request/URL/-1" />
+</head>
+
 <body metal:fill-slot="contents">
 
   <div class="documentation"

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/browser/zcml.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -55,7 +55,7 @@
     def fullTagName(self):
         context = removeSecurityProxy(self.context)
         ns, name = context.name
-        if context.prefixes[ns]:
+        if context.prefixes.get(ns):
             return '%s:%s' %(context.prefixes[ns], name)
         else:
             return name
@@ -78,7 +78,9 @@
             subDirective = directive
             directive = parent
         ns, name = directive.name
-        ns = quoteNS(ns)
+        # Sometimes ns is `None`, especially in the slug files, where no
+        # namespaces are used.
+        ns = quoteNS(ns or 'ALL')
         zcml = zapi.getUtility(IDocumentationModule, 'ZCML')
         if name not in zcml[ns]:
             ns = 'ALL'
@@ -89,7 +91,8 @@
         return link
 
     def objectURL(self, value, field, rootURL):
-        bound = field.bind(self.context.context)
+        naked = removeSecurityProxy(self.context)
+        bound = field.bind(naked.context)
         obj = bound.fromUnicode(value)
         if obj is None:
             return

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/class_.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -76,18 +76,20 @@
 
     def getAttributes(self):
         """See IClassDocumentation."""
-        return [(name, obj, iface) for name, obj, iface
-            in self._iterAllAttributes()
-            if not (ismethod(obj) or ismethoddescriptor(obj))]
+        return [(name, obj, iface)
+                for name, obj, iface in self._iterAllAttributes()
+                if not (ismethod(obj) or ismethoddescriptor(obj))]
 
     def getMethods(self):
         """See IClassDocumentation."""
-        return [(name, obj, iface) for name, obj, iface
-            in self._iterAllAttributes() if ismethod(obj)]
+        return [(name, obj, iface)
+                for name, obj, iface in self._iterAllAttributes()
+                if ismethod(obj)]
 
     def getMethodDescriptors(self):
-        return [(name, obj, iface) for name, obj, iface
-            in self._iterAllAttributes() if ismethoddescriptor(obj)]
+        return [(name, obj, iface)
+                for name, obj, iface in self._iterAllAttributes()
+                if ismethoddescriptor(obj)]
 
     def getSecurityChecker(self):
         """See IClassDocumentation."""

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/interfaces.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -30,7 +30,7 @@
 
     The utilities will be simple strings, representing the modules Python
     dotted name.
-    """ 
+    """
 
 class IModuleDocumentation(IReadContainer):
     """Representation of a Python module for documentation.

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/module.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/module.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/module.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -96,8 +96,7 @@
                    attr.__module__ == self._module.__name__:
 
                 if not hasattr(attr, '__name__') or \
-                       attr.__name__ != name or \
-                       name.startswith('_'):
+                       attr.__name__ != name:
                     continue
 
                 if isinstance(attr, (types.ClassType, types.TypeType)):
@@ -150,4 +149,8 @@
 
     def items(self):
         """See zope.app.container.interfaces.IReadContainer."""
-        return self._children.items()
+        # Only publicize public objects, even though we do keep track of
+        # private ones
+        return [(name, value)
+                for name, value in self._children.items()
+                if not name.startswith('_')]

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/text.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/text.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/text.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Function representation for code browser 
+"""Function representation for code browser
 
 $Id: __init__.py 29143 2005-02-14 22:43:16Z srichter $
 """
@@ -29,7 +29,7 @@
         self.__name__ = name
 
     def getContent(self):
-        file = open(self.path, 'r')
+        file = open(self.path, 'rb')
         content = file.read()
         file.close()
-        return content
+        return content.decode('utf-8')

Modified: Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/codemodule/zcml.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -16,6 +16,7 @@
 $Id$
 """
 __docformat__ = "reStructuredText"
+import copy
 from xml.sax import make_parser
 from xml.sax.xmlreader import InputSource
 from xml.sax.handler import feature_namespaces
@@ -41,6 +42,10 @@
     def startPrefixMapping(self, prefix, uri):
         self.prefixes[uri] = prefix
 
+    def evaluateCondition(self, expression):
+        # We always want to process/show all ZCML directives.
+        return True
+
     def startElementNS(self, name, qname, attrs):
         # The last stack item is parent of the stack item that we are about to
         # create
@@ -105,8 +110,12 @@
         self.__name__ = name
 
     def rootElement(self):
-        # Get the context that was originally generated during startup.
-        context = zope.app.appsetup.appsetup.getConfigContext()
+        # Get the context that was originally generated during startup and
+        # create a new context using its registrations
+        real_context = zope.app.appsetup.appsetup.getConfigContext()
+        context = config.ConfigurationMachine()
+        context._registry = copy.copy(real_context._registry)
+        context._features = copy.copy(real_context._features)
         context.package = self.package
 
         # Shut up i18n domain complaints

Modified: Zope3/trunk/src/zope/app/apidoc/component.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/component.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/component.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -231,8 +231,16 @@
     # provided interface id
     iface_id = '%s.%s' % (reg.provided.__module__, reg.provided.getName())
 
+    # Determine the URL
+    if isinstance(component, InterfaceClass):
+        url = 'Interface/%s' %path
+    else:
+        url = None
+        if isReferencable(path):
+            url = 'Code/%s' % path.replace('.', '/')
+
     return {'name': reg.name or _('<i>no name</i>'),
             'url_name': utilitymodule.encodeName(reg.name or '__noname__'),
             'iface_id': iface_id,
             'path': path,
-            'url': isReferencable(path) and path.replace('.', '/') or None}
+            'url': url}

Modified: Zope3/trunk/src/zope/app/apidoc/component.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/component.txt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/component.txt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -344,5 +344,5 @@
   {'iface_id': 'zope.app.apidoc.doctest.IFooBar',
    'name': u'<i>no name</i>',
    'path': 'zope.app.apidoc.doctest.MyFooBar',
-   'url': 'zope/app/apidoc/doctest/MyFooBar',
+   'url': 'Code/zope/app/apidoc/doctest/MyFooBar',
    'url_name': 'X19ub25hbWVfXw=='}

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -16,6 +16,7 @@
 $Id$
 """
 __docformat__ = 'restructuredtext'
+import inspect
 from zope.interface import Interface
 
 from zope.publisher.interfaces import IRequest
@@ -78,8 +79,8 @@
         # We must remove all proxies here, so that we get the context's
         # __module__ attribute. If we only remove security proxies, the
         # location proxy's module will be returned.
-        return renderText(self.context.__doc__,
-                          removeSecurityProxy(self.context).__module__)
+        iface = removeAllProxies(self.context)
+        return renderText(iface.__doc__, inspect.getmodule(iface))
 
     def getBases(self):
         """Get all bases of this class
@@ -91,7 +92,10 @@
           >>> details.getBases()
           ['zope.interface.Interface']
         """
-        return [getPythonPath(base) for base in self.context.__bases__]
+        # Persistent interfaces are security proxied, so we need to strip the
+        # security proxies off
+        iface = removeSecurityProxy(self.context)
+        return [getPythonPath(base) for base in iface.__bases__]
 
     def getTypes(self):
         """Return a list of interface types that are specified for this

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/component_macros.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -90,7 +90,7 @@
     <span i18n:translate="">Component:</span>
     <code style="font-size: 100%">
       <a href=""
-         tal:attributes="href string:$rootURL/Code/${utility/url}/index.html"
+         tal:attributes="href string:$rootURL/${utility/url}/index.html"
          tal:content="utility/path"
          tal:condition="utility/url" />
       <span tal:condition="not: utility/url" tal:replace="utility/path" />

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -22,6 +22,7 @@
       permission="zope.View"
       class=".macros.InterfaceDetailsMacros"
       allowed_interface="zope.interface.common.mapping.IItemMapping"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <browser:page
@@ -29,6 +30,7 @@
       name="iface_macros"
       permission="zope.View"
       template="iface_macros.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <browser:page
@@ -36,6 +38,7 @@
       name="component_macros"
       permission="zope.View"
       template="component_macros.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <browser:page
@@ -43,6 +46,7 @@
       name="presentation_macros"
       permission="zope.View"
       template="presentation_macros.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <!-- The name for the interface content cannot be 'index.html', since -->
@@ -54,6 +58,7 @@
       class=".browser.InterfaceDetails"
       name="index.html"
       template="index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <!-- Interface Documentation Module Menu -->
@@ -64,6 +69,7 @@
       class=".menu.Menu"
       name="menu.html"
       template="menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <browser:page
@@ -72,6 +78,7 @@
       class=".menu.Menu"
       name="staticmenu.html"
       template="static_menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
 
   <preferenceGroup

Modified: Zope3/trunk/src/zope/app/apidoc/interface.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/interface.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/interface.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -17,12 +17,14 @@
 """
 __docformat__ = 'restructuredtext'
 
+import inspect
+
 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
+from zope.app.apidoc.utilities import getPythonPath, renderText, getDocFormat
 
 
 def getElements(iface, type=IElement):
@@ -72,7 +74,7 @@
     # Remove interfaces provided by every interface instance
     types.remove(ISpecification)
     types.remove(IElement)
-    types.remove(Interface)    
+    types.remove(Interface)
     # Remove interface provided by every interface type
     types.remove(IInterface)
     return types
@@ -94,25 +96,33 @@
             return iface
 
     # If not even a `IField`-based interface was found, return the first
-    # interface of the implemented interfaces list.  
+    # interface of the implemented interfaces list.
     return field_iface or ifaces[0]
-    
 
-def getAttributeInfoDictionary(attr, format='zope.source.rest'):
+
+def _getDocFormat(attr):
+    module = inspect.getmodule(attr.interface)
+    return getDocFormat(module)
+
+
+def getAttributeInfoDictionary(attr, format=None):
     """Return a page-template-friendly information dictionary."""
+    format = format or _getDocFormat(attr)
     return {'name': attr.getName(),
             'doc': renderText(attr.getDoc() or u'', format=format)}
 
 
-def getMethodInfoDictionary(method, format='zope.source.rest'):
+def getMethodInfoDictionary(method, format=None):
     """Return a page-template-friendly information dictionary."""
+    format = format or _getDocFormat(method)
     return {'name': method.getName(),
             'signature': method.getSignatureString(),
             'doc': renderText(method.getDoc() or u'', format=format)}
 
 
-def getFieldInfoDictionary(field, format='zope.source.rest'):
+def getFieldInfoDictionary(field, format=None):
     """Return a page-template-friendly information dictionary."""
+    format = format or _getDocFormat(field)
 
     info = {'name': field.getName(),
             'required': field.required,

Modified: Zope3/trunk/src/zope/app/apidoc/interface.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/interface.txt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/interface.txt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -129,7 +129,7 @@
 interface.
 
   >>> interface.getFields(IFoo) #doctest: +ELLIPSIS
-  [('foo', <zope.schema._bootstrapfields.Field object at ...>), 
+  [('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
@@ -225,7 +225,7 @@
 This function returns a page-template-friendly dictionary for a method:
 
   >>> pprint(interface.getMethodInfoDictionary(IFoo['blah'])) #doc
-  {'doc': 
+  {'doc':
      u'<p>This is the <cite>blah</cite> method.</p>\n',
    'name': 'blah',
    'signature': '(one, two, three=None, *args, **kwargs)'}

Modified: Zope3/trunk/src/zope/app/apidoc/static.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/static.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/static.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -22,18 +22,18 @@
 import sys
 import time
 import urllib2
+import warnings
 import HTMLParser
 
 import zope.testbrowser
 import mechanize
 
 from zope.app.testing import functional
-from zope.deprecation import __show__
 
 from zope.app.apidoc import classregistry
 
 # Setup the user feedback detail level.
-VERBOSITY = 4
+VERBOSITY = 5
 
 VERBOSITY_MAP = {1: 'ERROR', 2: 'WARNING', 3: 'INFO'}
 
@@ -48,6 +48,8 @@
 USERNAME = 'mgr'
 PASSWORD = 'mgrpw'
 
+DEBUG = False
+
 # A mapping of HTML elements that can contain links to the attribute that
 # actually contains the link
 urltags = {
@@ -196,12 +198,15 @@
     def start(self):
         """Start the retrieval of the apidoc."""
         t0 = time.time()
-        __show__.off()
 
         self.visited = []
         self.counter = 0
-        self.errors = 0
+        self.linkErrors = 0
+        self.htmlErrors = 0
 
+        # Turn off deprecation warnings
+        warnings.filterwarnings("ignore", category=DeprecationWarning)
+
         if not os.path.exists(self.rootDir):
             os.mkdir(self.rootDir)
 
@@ -212,10 +217,13 @@
 
         self.browser.setUserAndPassword(USERNAME, PASSWORD)
         self.browser.urltags = urltags
-        #self.browser.addheaders.append(('X-zope-handle-errors', False))
 
-        classregistry.IGNORE_MODULES = ['twisted']
+        if DEBUG:
+            self.browser.addheaders.append(('X-zope-handle-errors', False))
 
+        classregistry.IGNORE_MODULES = ['twisted',
+                                        'zope.app.twisted.ftp.test']
+
         # Work through all links until there are no more to work on.
         self.sendMessage('Starting retrieval.')
         while self.linkQueue:
@@ -227,16 +235,16 @@
                 self.showStatistics(link)
                 self.processLink(link)
 
-        __show__.on()
         t1 = time.time()
 
-        self.sendMessage("Run time: %.3f sec real" % (t1-t0))
+        self.sendMessage("Run time: %.3f sec" % (t1-t0))
         self.sendMessage("Links: %i" %self.counter)
-        self.sendMessage("Errors: %i" %self.errors)
+        self.sendMessage("Link Retrieval Errors: %i" %self.linkErrors)
+        self.sendMessage("HTML ParsingErrors: %i" %self.htmlErrors)
 
     def showStatistics(self, link):
         self.counter += 1
-        if VERBOSITY >= 3:
+        if VERBOSITY >= 5:
             url = link.absoluteURL[-(self.maxWidth):]
             sys.stdout.write('\r' + ' '*(self.maxWidth+13))
             sys.stdout.write('\rLink %5d: %s' % (self.counter, url))
@@ -265,21 +273,29 @@
             self.browser.open(link.callableURL)
         except urllib2.HTTPError, error:
             # Something went wrong with retrieving the page.
-            self.errors += 1
+            self.linkErrors += 1
             self.sendMessage(
                 '%s (%i): %s' % (error.msg, error.code, link.callableURL), 2)
             self.sendMessage('+-> Reference: ' + link.referenceURL, 2)
-            return
+            # Now set the error page as the response
+            from ClientCookie._Util import response_seek_wrapper
+            self.browser._response = response_seek_wrapper(error)
         except (urllib2.URLError, ValueError):
             # We had a bad URL running the publisher browser
-            self.errors += 1
+            self.linkErrors += 1
             self.sendMessage('Bad URL: ' + link.callableURL, 2)
             self.sendMessage('+-> Reference: ' + link.referenceURL, 2)
             return
-        #except Exception, error:
-        #    import pdb; pdb.set_trace()
-        #    return
+        except Exception, error:
+            # This should never happen outside the debug mode. We really want
+            # to catch all exceptions, so that we can investigate them.
+            if DEBUG:
+                import pdb; pdb.set_trace()
+            return
 
+        # Get the response content
+        contents = self.browser.contents
+
         # Make sure the directory exists and get a file path.
         relativeURL = url.replace(URL, '')
         dir = self.rootDir
@@ -293,16 +309,16 @@
 
         filepath = os.path.join(dir, filename)
 
-        # Get the response content
-        contents = self.browser.contents
-
         # Now retrieve all links
         if self.browser.viewing_html():
 
             try:
                 links = self.browser.links()
-            except HTMLParser.HTMLParseError:
+            except HTMLParser.HTMLParseError, error:
+                self.htmlErrors += 1
                 self.sendMessage('Failed to parse HTML: ' + url, 1)
+                self.sendMessage('+-> %s: line %i, column %s' % (
+                    error.msg, error.lineno, error.offset), 1)
                 links = []
 
             links = [Link(mech_link, url) for mech_link in links]
@@ -322,9 +338,15 @@
                 contents = contents.replace(link.originalURL, '/'.join(parts))
 
         # Write the data into the file
-        file = open(filepath, 'w')
-        file.write(contents)
-        file.close()
+        try:
+            file = open(filepath, 'w')
+            file.write(contents)
+            file.close()
+        except IOError:
+            # The file already exists, so it is a duplicate and a bad one,
+            # since the URL misses `index.hml`. ReST can produce strange URLs
+            # that produce this problem, and we have little control over it.
+            pass
 
         # Cleanup; this is very important, otherwise we are opening too many
         # files.

Modified: Zope3/trunk/src/zope/app/apidoc/typemodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/typemodule/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/typemodule/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -8,7 +8,7 @@
   </class>
 
   <class class=".type.TypeInterface">
-    <allow interface="zope.app.container.interfaces.IReadContainer" 
+    <allow interface="zope.app.container.interfaces.IReadContainer"
            attributes="interface" />
   </class>
 
@@ -25,14 +25,16 @@
       class=".browser.Menu"
       name="menu.html"
       template="menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
       />
-      
+
   <browser:page
       for=".type.TypeModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.Menu"
       name="staticmenu.html"
       template="static_menu.pt"
-      />      
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
 </configure>

Modified: Zope3/trunk/src/zope/app/apidoc/utilities.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilities.py	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/utilities.py	2005-10-31 05:16:32 UTC (rev 39760)
@@ -34,11 +34,13 @@
 from zope.app.i18n import ZopeMessageFactory as _
 from zope.app.container.interfaces import IReadContainer
 
-from zope.app.apidoc.classregistry import safe_import
+from zope.app.apidoc.classregistry import safe_import, IGNORE_MODULES
 
 _remove_html_overhead = re.compile(
     r'(?sm)^<html.*<body.*?>\n(.*)</body>\n</html>\n')
 
+space_re = re.compile('\n^( *)\S', re.M)
+
 _marker = object()
 
 BASEDIR = dirname(dirname(dirname(zope.__file__)))
@@ -114,14 +116,32 @@
     # Sometimes no path exists, so make a simple check first; example: None
     if path is None:
         return False
-    module_name, obj_name = path.rsplit('.', 1)
+
+    # There are certain paths that we do not want to reference, most often
+    # because they are outside the scope of this documentation
+    for exclude_name in IGNORE_MODULES:
+        if path.startswith(exclude_name):
+            return False
+    split_path = path.rsplit('.', 1)
+    if len(split_path) == 2:
+        module_name, obj_name = split_path
+    else:
+        module_name, obj_name = split_path[0], None
+
     # Do not allow private attributes to be accessible
-    if (obj_name.startswith('_') and
+    if (obj_name is not None and
+        obj_name.startswith('_') and
         not (obj_name.startswith('__') and obj_name.endswith('__'))):
         return False
     module = safe_import(module_name)
     if module is None:
         return False
+
+    # If the module imported correctly and no name is provided, then we are
+    # all good.
+    if obj_name is None:
+        return True
+
     obj = getattr(module, obj_name, _marker)
     if obj is _marker:
         return False
@@ -271,27 +291,17 @@
     """Convert a module's __docformat__ specification to a renderer source
     id"""
     format = getattr(module, '__docformat__', 'structuredtext').lower()
+    # The format can also contain the language, so just get the first part
+    format = format.split(' ')[0]
     return _format_dict.get(format, 'zope.source.stx')
 
 
 def dedentString(text):
     """Dedent the docstring, so that docutils can correctly render it."""
-    dedent = 0
-    lines = text.split('\n')
-    for line in lines[1:]:
-        if line != '':
-            for char in line:
-                if char == ' ':
-                    dedent += 1
-                else:
-                    break
-            break
+    dedent = min([len(match) for match in space_re.findall(text)] or [0])
+    return re.compile('\n {%i}' % dedent, re.M).sub('\n', text)
 
-    for index in range(1, len(lines)):
-        lines[index] = lines[index][dedent:]
-    return '\n'.join(lines)
 
-
 def renderText(text, module=None, format=None, dedent=True):
     if not text:
         return u''

Modified: Zope3/trunk/src/zope/app/apidoc/utilities.txt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilities.txt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/utilities.txt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -213,6 +213,17 @@
 be referenced somehow. But that's not true, partially by design of apidoc, but
 also due to limitations of the Python language itself.
 
+First, here are some cases that work:
+
+  >>> utilities.isReferencable('zope')
+  True
+  >>> utilities.isReferencable('zope.app')
+  True
+  >>> utilities.isReferencable('zope.app.apidoc.apidoc.APIDocumentation')
+  True
+  >>> utilities.isReferencable('zope.app.apidoc.apidoc.handleNamespace')
+  True
+
 The first case is ``None``. When you ask for the python path of ``None``, you
 get ``None``, so that result should not be referencable:
 
@@ -249,7 +260,7 @@
   >>> utilities.isReferencable(path)
   False
 
-Finally, since API doc does not allow the documentation of instances yet, it
+Next, since API doc does not allow the documentation of instances yet, it
 is not possible to document singletons, so they are not referencable:
 
   >>> class Singelton(object):
@@ -263,7 +274,26 @@
   >>> utilities.isReferencable('zope.app.apidoc.doctest.Singelton')
   False
 
+Finally, the global ``IGNORE_MODULES`` list from the class registry is also
+used to give a negative answer. If a module is listed in ``IGNORE_MODULES``,
+then ``False`` is returned.
 
+  >>> import classregistry
+  >>> classregistry.IGNORE_MODULES.append('zope.app.apidoc')
+
+  >>> utilities.isReferencable('zope.app')
+  True
+  >>> utilities.isReferencable('zope.app.apidoc')
+  False
+  >>> utilities.isReferencable('zope.app.apidoc.apidoc.APIDocumentation')
+  False
+
+  >>> classregistry.IGNORE_MODULES.pop()
+  'zope.app.apidoc'
+  >>> utilities.isReferencable('zope.app.apidoc')
+  True
+
+
 `getPermissionIds(name, checker=_marker, klass=_marker)`
 --------------------------------------------------------
 
@@ -609,7 +639,17 @@
 endorse restructured text, so that many files are still in the structured text
 format. All converted and new modules will have the `__docformat__` attribute.
 
+The `__docformat__` attribute can also optionally specify a language field. We
+simply ignore it:
 
+  >>> class Module(object):
+  ...     pass
+  >>> module = Module()
+  >>> module.__docformat__ = 'restructuredtext en'
+  >>> utilities.getDocFormat(module)
+  'zope.source.rest'
+
+
 `dendentString(text)`
 ---------------------
 
@@ -655,7 +695,8 @@
 
 Again, the whitespace was removed only after the first line. Also note that
 the function determines the indentation level correctly. So what happens if
-there are multiple indentation levels? Well, the first occurrence wins:
+there are multiple indentation levels? The smallest amount of indentation is
+chosen:
 
   >>> def func():
   ...     '''Short description
@@ -673,7 +714,25 @@
     Second Level
   <BLANKLINE>
 
+  >>> def func():
+  ...     '''Short description
+  ...
+  ...       $$$ print 'example'
+  ...       example
+  ...
+  ...     And now the description.
+  ...     '''
 
+  >>> print utilities.dedentString(func.__doc__)
+  Short description
+  <BLANKLINE>
+    $$$ print 'example'
+    example
+  <BLANKLINE>
+  And now the description.
+  <BLANKLINE>
+
+
 `renderText(text, module=None, format=None)`
 --------------------------------------------
 

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -26,20 +26,26 @@
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.Menu"
       name="menu.html"
-      template="menu.pt" />
+      template="menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
   <browser:page
       for=".utilitymodule.UtilityModule"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.Menu"
       name="staticmenu.html"
-      template="static_menu.pt" />
+      template="static_menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
   <browser:page
       for=".utilitymodule.Utility"
       permission="zope.app.apidoc.UseAPIDoc"
       class=".browser.UtilityDetails"
       name="index.html"
-      template="index.pt" />
+      template="index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
 </configure>

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/index.pt	2005-10-31 05:16:32 UTC (rev 39760)
@@ -7,7 +7,7 @@
   <h1 class="details-header">
     <a href=""
        tal:attributes="
-           href string:../../../Interface/${iface/getId}/index.html"
+           href string:$rootURL/Interface/${iface/getId}/index.html"
        tal:content="iface/getId" /> <br />
     <tal:block i18n:translate="">(Name: "<span
         tal:replace="view/getName" i18n:name="name" />")</tal:block>
@@ -20,8 +20,12 @@
       <span i18n:translate="">Component:</span>
       <a href=""
          tal:attributes="href
-             string:../../../Code/${component/url}/index.html"
-         tal:content="component/path" /></h3>
+             string:$rootURL/${component/url}/index.html"
+         tal:content="component/path"
+         tal:condition="component/url" />
+      <span tal:replace="component/path"
+            tal:condition="not: component/url" />
+    </h3>
   </div>
 
   <div class="indent">

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/configure.zcml	2005-10-31 05:03:42 UTC (rev 39759)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/configure.zcml	2005-10-31 05:16:32 UTC (rev 39760)
@@ -17,30 +17,36 @@
   </class>
 
   <utility
-    provides="zope.app.apidoc.interfaces.IDocumentationModule"
-    factory=".ZCMLModule"
-    name="ZCML" />
+      provides="zope.app.apidoc.interfaces.IDocumentationModule"
+      factory=".ZCMLModule"
+      name="ZCML" />
 
   <browser:page
-    for=".ZCMLModule"
-    class=".browser.Menu"
-    permission="zope.app.apidoc.UseAPIDoc"
-    name="menu.html"
-    template="menu.pt" />
+      for=".ZCMLModule"
+      class=".browser.Menu"
+      permission="zope.app.apidoc.UseAPIDoc"
+      name="menu.html"
+      template="menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
   <browser:page
-    for=".ZCMLModule"
-    class=".browser.Menu"
-    permission="zope.app.apidoc.UseAPIDoc"
-    name="staticmenu.html"
-    template="static_menu.pt" />
+      for=".ZCMLModule"
+      class=".browser.Menu"
+      permission="zope.app.apidoc.UseAPIDoc"
+      name="staticmenu.html"
+      template="static_menu.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
   <browser:page
-    for=".Directive"
-    class=".browser.DirectiveDetails"
-    permission="zope.app.apidoc.UseAPIDoc"
-    name="index.html"
-    template="index.pt" />
+      for=".Directive"
+      class=".browser.DirectiveDetails"
+      permission="zope.app.apidoc.UseAPIDoc"
+      name="index.html"
+      template="index.pt"
+      layer="zope.app.apidoc.browser.skin.apidoc"
+      />
 
 </configure>
 



More information about the Zope3-Checkins mailing list