[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/apidoc/ Backported removal of removeAllProxies(). But I had to add a couple calls

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Aug 26 09:23:58 EDT 2004


Log message for revision 27276:
  Backported removal of removeAllProxies(). But I had to add a couple calls 
  back, since the location proxy's module was used to determine the doc 
  format. This passed in the branch, since the doc format there has not been 
  converted to re-structured text. I will have to merge the fixes here to 
  the branch in a moment.
   --This line, and those below, will be ignored--
  
  M    app/apidoc/viewmodule/configure.zcml
  M    app/apidoc/viewmodule/__init__.py
  M    app/apidoc/utilities.py
  M    app/apidoc/servicemodule/browser.py
  M    app/apidoc/utilitymodule/browser.py
  M    app/apidoc/zcmlmodule/index.pt
  M    app/apidoc/zcmlmodule/browser.py
  M    app/apidoc/classmodule/browser.py
  M    app/apidoc/ifacemodule/browser.py
  M    app/apidoc/ifacemodule/menu.py
  


Changed:
  U   Zope3/trunk/src/zope/app/apidoc/classmodule/browser.py
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py
  U   Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py
  U   Zope3/trunk/src/zope/app/apidoc/servicemodule/browser.py
  U   Zope3/trunk/src/zope/app/apidoc/utilities.py
  U   Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py
  U   Zope3/trunk/src/zope/app/apidoc/viewmodule/__init__.py
  U   Zope3/trunk/src/zope/app/apidoc/viewmodule/configure.zcml
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py
  U   Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt


-=-
Modified: Zope3/trunk/src/zope/app/apidoc/classmodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/classmodule/browser.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/classmodule/browser.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -132,7 +132,10 @@
     def __init__(self, context, request):
         self.context = context
         self.request = request
-        package = removeAllProxies(zapi.getParent(context))._Module__module
+        # 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 = zapi.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
@@ -364,7 +367,7 @@
     """Represents the details of the function."""
 
     def getDocString(self):
-        r"""Get the doc string of the class in a rendered format.
+        r"""Get the doc string of the function in a rendered format.
 
         Example::
 
@@ -379,7 +382,7 @@
 
 
     def getAttributes(self):
-        """Get all attributes of this class.
+        """Get all attributes of this function.
 
         Example::
 
@@ -394,14 +397,12 @@
            ('type_link', '__builtin__/bool'),
            ('value', 'True')]
         """
-        attrs = []
-        func = removeAllProxies(self.context)
         return [{'name': name,
                  'value': `attr`,
                  'type': type(attr).__name__,
                  'type_link': _getTypePath(type(attr)).replace('.', '/')}
                 
-                for name, attr in func.getAttributes()]
+                for name, attr in self.context.getAttributes()]
 
 
 class ClassDetails(object):
@@ -546,15 +547,17 @@
            ('write_perm', None)]
         """
         attrs = []
-        klass = removeAllProxies(self.context)
-        for name, attr, iface in klass.getAttributes():
+        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)}
-            checker = removeAllProxies(klass.getSecurityChecker())
-            entry.update(getPermissionIds(name, checker))
+            # 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, zapi.removeSecurityProxy(checker)))
             attrs.append(entry)
         return attrs
 
@@ -589,7 +592,12 @@
             ('write_perm', None)]]
         """
         methods = []
-        klass = removeAllProxies(self.context)
+        # 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 = zapi.removeSecurityProxy(self.context)
         for name, attr, iface in klass.getMethods():
             entry = {'name': name,
                      'signature': getFunctionSignature(attr),
@@ -676,10 +684,10 @@
         """
         entries = [{'name': name,
                     'url': zapi.getView(obj, 'absolute_url', self.request)(),
-                    'ismodule': type(removeAllProxies(obj)) is Module,
-                    'isclass': type(removeAllProxies(obj)) is Class,
-                    'isfunction': type(removeAllProxies(obj)) is Function,
-                    'iszcmlfile': type(removeAllProxies(obj)) is ZCMLFile,}
+                    '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:
@@ -710,7 +718,8 @@
         names = self.context.getPath().split('.') 
         crumbs = []
         module = self.context
-        while type(removeAllProxies(module)) is Module:
+        # I really need the class here, so remove the proxy.
+        while zapi.removeSecurityProxy(module).__class__ is Module:
             crumbs.append(
                 {'name': zapi.name(module),
                  'url': zapi.getView(module, 'absolute_url', self.request)()}

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/browser.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -53,6 +53,11 @@
       >>> 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:
@@ -127,7 +132,6 @@
       [('id', 'zope.app.apidoc.ifacemodule.browser.ISpecialField'),
        ('name', 'ISpecialField')]
     """
-    field = removeAllProxies(field)
     # This is bad, but due to bootstrapping, directlyProvidedBy does
     # not work 
     name = field.__class__.__name__
@@ -174,7 +178,7 @@
       [('name', 'ExtraField'),
        ('path', 'zope/app/apidoc/ifacemodule/browser/ExtraField')]
     """
-    class_ = removeAllProxies(field).__class__
+    class_ = field.__class__
     return {'name': class_.__name__,
             'path': getPythonPath(class_).replace('.', '/')}
 
@@ -239,7 +243,8 @@
           '<h1>This is the Foo interface</h1>'
         """
         # We must remove all proxies here, so that we get the context's
-        # __module__ attribute and not the proxy's. 
+        # __module__ attribute. If we only remove security proxies, the
+        # location proxy's module will be returned.
         return renderText(self.context.__doc__,
                           removeAllProxies(self.context).__module__)
 
@@ -264,7 +269,7 @@
         Example::
 
           >>> from zope.app.apidoc.tests import pprint
-          >>> from tests import getInterfaceDetails
+          >>> from tests import getInterfaceDetails, IFoo
           >>> from zope.interface import Interface, directlyProvides
           >>> class IType(Interface):
           ...     pass
@@ -273,7 +278,7 @@
           >>> details.getTypes()
           []
 
-          >>> directlyProvides(removeAllProxies(details.context), IType)
+          >>> directlyProvides(IFoo, IType)
           >>> type = details.getTypes()[0]
           >>> pprint(type)
           [('name', 'IType'),
@@ -281,8 +286,12 @@
 
           Cleanup
 
-          >>> directlyProvides(removeAllProxies(details.context), [])
+          >>> directlyProvides(IFoo, [])
         """
+        # 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)
@@ -304,6 +313,10 @@
           [[('doc', '<p>This is bar.</p>\n'), ('name', 'bar')],
            [('doc', '<p>This is foo.</p>\n'), ('name', 'foo')]]
         """
+        # 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:
@@ -332,10 +345,15 @@
             ('name', 'get'),
             ('signature', '(key, default=None)')]]
         """        
+        # 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__)}
+                 'doc': renderText(
+                            method.getDoc() or '',
+                            removeAllProxies(self.context).__module__)}
                 for method in _get(self.context, IMethod).values()]
             
     def getFields(self):
@@ -369,6 +387,10 @@
             ('name', 'description'),
             ('required', u'optional')]]
         """
+        # 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.
         fields = map(lambda x: x[1], _getInOrder(self.context, IField))
         return [{'name': field.getName(),
                  'iface': _getFieldInterface(field),
@@ -407,7 +429,9 @@
             ('required', [])]]
         """
         service = zapi.getService('Adapters')
-        iface = removeAllProxies(self.context)
+        # Must remove security proxies, so that we have access to the API
+        # methods. 
+        iface = zapi.removeSecurityProxy(self.context)
         adapters = []
         for reg in service.registrations():
             # Only grab the adapters for which this interface is required
@@ -448,6 +472,8 @@
             ('required', ['zope.app.apidoc.ifacemodule.tests.IBar'])]]
         """
         service = zapi.getService('Adapters')
+        # 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 service.registrations():
@@ -482,6 +508,8 @@
           [[('path', 'zope.app.apidoc.ifacemodule.tests.Foo'),
             ('url', 'zope/app/apidoc/ifacemodule/tests/Foo')]]
         """
+        # 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)
         return [{'path': path, 'url': path.replace('.', '/')}
@@ -506,6 +534,8 @@
             ('title', 'Foo Factory'),
             ('url', 'zope/component/factory/Factory')]]
         """
+        # 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)
@@ -539,6 +569,8 @@
             ('url_name', u'The Foo')]]
         """
         service = zapi.getService('Utilities')
+        # Must remove security and location proxies, so that we have access to
+        # the API methods and class representation.
         utils = service.getUtilitiesFor(removeAllProxies(self.context))
         info = []
         for name, util in utils:
@@ -563,6 +595,8 @@
           >>> details.getServices()
           ['Foo']
         """
+        # Must remove security and location proxies, so that we have access to
+        # the API methods and class representation.
         iface = removeAllProxies(self.context)
         service = zapi.getService('Services')
         services = service.getServiceDefinitions()

Modified: Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/ifacemodule/menu.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -22,7 +22,6 @@
 __docformat__ = 'restructuredtext'
 
 from zope.interface import implements
-from zope.proxy import removeAllProxies
 
 from zope.app import zapi
 from zope.app.location.interfaces import ILocation
@@ -114,7 +113,7 @@
     def getChildObjects(self):
         """See zope.app.tree.interfaces.IChildObject"""
         objects = {}
-        names = removeAllProxies(self.context.keys())
+        names = zapi.removeSecurityProxy(self.context.keys())
         names.sort()
         for name in names:
             # Split these long names and make part of the module path separate
@@ -167,13 +166,13 @@
 
     def getMenuTitle(self, node):
         """Return the title of the node that is displayed in the menu."""
-        if isinstance(removeAllProxies(node.context.__parent__), Module):
+        if zapi.isinstance(node.context.__parent__, Module):
             parent = node.context.__parent__
             return zapi.name(node.context).replace(zapi.name(parent)+'.', '')
         return zapi.name(node.context)
 
     def getMenuLink(self, node):
         """Return the HTML link of the node that is displayed in the menu."""
-        if isinstance(removeAllProxies(node.context), Module):
+        if zapi.isinstance(node.context, Module):
             return None
         return './' + zapi.name(node.context) + '/apiindex.html'

Modified: Zope3/trunk/src/zope/app/apidoc/servicemodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/servicemodule/browser.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/servicemodule/browser.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -17,8 +17,6 @@
 """
 __docformat__ = 'restructuredtext'
 
-from zope.proxy import removeAllProxies
-
 from zope.app import zapi
 from zope.app.location import LocationProxy
 from zope.app.apidoc.ifacemodule.browser import InterfaceDetails
@@ -105,8 +103,7 @@
     
     def implementations(self):
         """Retrieve a list of implementations of this service."""
-        impl = map(removeAllProxies, self.context.implementations)
-        impl = map(lambda x: x.__class__, self.context.implementations)
+        impl = [impl.__class__ for impl in self.context.implementations]
         return [{'path': getPythonPath(klass),
                  'url': getPythonPath(klass).replace('.', '/')}
                 for klass in impl]

Modified: Zope3/trunk/src/zope/app/apidoc/utilities.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilities.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/utilities.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -25,7 +25,6 @@
 
 import zope
 from zope.interface import implements, implementedBy
-from zope.proxy import removeAllProxies
 from zope.publisher.browser import TestRequest
 from zope.security.checker import getCheckerForInstancesOf, Global
 from zope.security.interfaces import INameBasedChecker
@@ -123,6 +122,9 @@
     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
@@ -151,15 +153,19 @@
     """
     if obj is None:
         return None
-    if hasattr(obj, "im_class"):
-        obj = obj.im_class
-    module = obj.__module__
-    return '%s.%s' %(module, obj.__name__)
 
+    # Even for methods `im_class` and `__module__` is not allowed to be
+    # accessed (which is probably not a bad idea). So, we remove the security
+    # proxies for this check.
+    naked = zapi.removeSecurityProxy(obj)
+    if hasattr(naked, "im_class"):
+        naked = naked.im_class
+    module = naked.__module__
+    return '%s.%s' %(module, naked.__name__)
 
+
 def _evalId(id):
-    id = removeAllProxies(id)
-    if isinstance(id, Global):
+    if zapi.isinstance(id, Global):
         id = id.__name__
         if id == 'CheckerPublic':
             id = 'zope.Public'

Modified: Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/utilitymodule/browser.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -23,7 +23,6 @@
 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.proxy import removeAllProxies
 
 class UtilityDetails(object):
     """Utility Details View."""
@@ -106,13 +105,11 @@
           [('path', 'zope.app.apidoc.utilitymodule.browser.Bar'),
            ('url', 'zope/app/apidoc/utilitymodule/browser/Bar')]
         """
-        component = removeAllProxies(self.context.component)
-        klass = type(component)
+        # 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 
+        klass = self.context.component.__class__
 
-        # Support for old-style classes
-        if klass == InstanceType:
-            klass = component.__class__
-
         return {'path': getPythonPath(klass),
                 'url':  getPythonPath(klass).replace('.', '/')}
 
@@ -165,19 +162,19 @@
 
     def getMenuTitle(self, node):
         """Return the title of the node that is displayed in the menu."""
-        obj = removeAllProxies(node.context)
+        obj = node.context
         if zapi.name(obj) == NONAME:
             return 'no name'
-        if isinstance(obj, UtilityInterface):
+        if zapi.isinstance(obj, UtilityInterface):
             return zapi.name(obj).split('.')[-1]
         return zapi.name(obj)
 
     def getMenuLink(self, node):
         """Return the HTML link of the node that is displayed in the menu."""
-        obj = removeAllProxies(node.context)
-        if isinstance(obj, Utility):
+        obj = node.context
+        if zapi.isinstance(obj, Utility):
             iface = zapi.getParent(obj)
             return './'+zapi.name(iface) + '/' + zapi.name(obj) + '/index.html'
-        if isinstance(obj, UtilityInterface):
+        if zapi.isinstance(obj, UtilityInterface):
             return '../Interface/'+zapi.name(obj) + '/apiindex.html'
         return None

Modified: Zope3/trunk/src/zope/app/apidoc/viewmodule/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/viewmodule/__init__.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/viewmodule/__init__.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -23,7 +23,6 @@
 from zope.component.presentation import LayerRegistration
 from zope.app.apidoc.interfaces import IDocumentationModule
 from zope.app.apidoc.utilities import relativizePath
-from zope.proxy import removeAllProxies
 from zope.app.i18n import ZopeMessageIDFactory as _
 
 # TODO: Temporary hack, since registering an adapter for a particular class is
@@ -183,7 +182,7 @@
         
         if 'default' in self.context.layers:
             default = LayerRegistration('default',
-                                        'This is a predefined skin.')
+                                        'This is a predefined layer.')
             layers.append(ILayerDocumentation(default))
         return layers
         
@@ -197,7 +196,7 @@
 
         # We can safely assume that for global skin registrations we have an
         # configuration info object.
-        info = removeAllProxies(self.context.doc)
+        info = self.context.doc
         doc = _('$file (line $line)')
         doc.mapping = {'file': relativizePath(info.file),
                        'line': info.line}
@@ -259,7 +258,7 @@
 
         # We can safely assume that for global layer registrations we have an
         # configuration info object.
-        info = removeAllProxies(self.context.doc)
+        info = self.context.doc
         doc = _('$file (line $line)')
         doc.mapping = {'file': relativizePath(info.file),
                        'line': info.line}

Modified: Zope3/trunk/src/zope/app/apidoc/viewmodule/configure.zcml
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/viewmodule/configure.zcml	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/viewmodule/configure.zcml	2004-08-26 13:23:57 UTC (rev 27276)
@@ -20,7 +20,9 @@
   <adapter
       for=".ISkinRegistration"
       provides=".ISkinDocumentation"
-      factory=".SkinDocumentation" />
+      factory=".SkinDocumentation"
+      permission="zope.app.apidoc.UseAPIDoc"
+      trusted="True" />
 
   <class class=".LayerDocumentation">
     <allow interface=".ILayerDocumentation" />
@@ -30,7 +32,9 @@
   <adapter
       for=".ILayerRegistration"
       provides=".ILayerDocumentation"
-      factory=".LayerDocumentation" />
+      factory=".LayerDocumentation"
+      permission="zope.app.apidoc.UseAPIDoc"
+      trusted="True" />
 
   <browser:page
       for=".ViewModule"

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/browser.py	2004-08-26 13:23:57 UTC (rev 27276)
@@ -17,7 +17,6 @@
 """
 __docformat__ = 'restructuredtext'
 
-from zope.proxy import removeAllProxies
 from zope.configuration.xmlconfig import ParserInfo
 
 from zope.app import zapi
@@ -71,8 +70,8 @@
 
     def getMenuTitle(self, node):
         """Return the title of the node that is displayed in the menu."""
-        obj = removeAllProxies(node.context)
-        if isinstance(obj, Namespace):
+        obj = node.context
+        if zapi.isinstance(obj, Namespace):
             name = obj.getShortName()
             if name == 'ALL':
                 return 'All Namespaces'
@@ -81,8 +80,8 @@
 
     def getMenuLink(self, node):
         """Return the HTML link of the node that is displayed in the menu."""
-        obj = removeAllProxies(node.context)
-        if isinstance(obj, Directive):
+        obj = node.context
+        if zapi.isinstance(obj, Directive):
             ns = zapi.getParent(obj)
             return './'+zapi.name(ns) + '/' + zapi.name(obj) + '/index.html'
         return None
@@ -165,8 +164,11 @@
            ('file', 'foo.zcml'),
            ('line', 2)]
         """
-        info = removeAllProxies(self.context.info)
-        if isinstance(info, ParserInfo):
+        # 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.  
+        info = zapi.removeSecurityProxy(self.context.info)
+        if zapi.isinstance(info, ParserInfo):
             return {'file': relativizePath(info.file),
                     'line': info.line,
                     'column': info.column,
@@ -215,8 +217,7 @@
            ('url', 'zope/app/apidoc/zcmlmodule/tests/foo')]
         """
         if self.context.handler is not None:
-            handler = removeAllProxies(self.context.handler)
-            path = getPythonPath(handler)
+            path = getPythonPath(self.context.handler)
             return {'path': path,
                     'url': path.replace('.', '/')}
         return None
@@ -264,7 +265,6 @@
             details.context = schema
             details.request = self.request
 
-            handler = removeAllProxies(handler)
             path = getPythonPath(handler)
             dirs.append({'namespace': ns,
                          'name': name,

Modified: Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt
===================================================================
--- Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt	2004-08-26 12:46:15 UTC (rev 27275)
+++ Zope3/trunk/src/zope/app/apidoc/zcmlmodule/index.pt	2004-08-26 13:23:57 UTC (rev 27276)
@@ -129,7 +129,7 @@
           <code tal:content="field/iface/name">IField</code></a>
       (<span tal:content="string:${field/required}, ">optional, </span>
         default = <code tal:content="field/default" />)<br>
-      <span tal:content="field/description">field desc</span>      
+      <span tal:content="structure field/description">field desc</span>      
     </li>
 
   </ul>



More information about the Zope3-Checkins mailing list