[Zope3-checkins] CVS: Zope3/src/zope/app/apidoc/classmodule - browser.py:1.3

Stephan Richter srichter at cosmos.phy.tufts.edu
Sun Mar 28 18:39:53 EST 2004


Update of /cvs-repository/Zope3/src/zope/app/apidoc/classmodule
In directory cvs.zope.org:/tmp/cvs-serv22206/src/zope/app/apidoc/classmodule

Modified Files:
	browser.py 
Log Message:


Added tests.



Added Menu view class to support the search for classes by partial path.



Moved a lot of the module and class introspection into the module classes.



Sorting entries into columns moved to utilities.




=== Zope3/src/zope/app/apidoc/classmodule/browser.py 1.2 => 1.3 ===
--- Zope3/src/zope/app/apidoc/classmodule/browser.py:1.2	Wed Feb 25 17:26:45 2004
+++ Zope3/src/zope/app/apidoc/classmodule/browser.py	Sun Mar 28 18:39:52 2004
@@ -11,7 +11,7 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Class Module Views
+"""Class Documentation Module Views
 
 $Id$
 """
@@ -20,150 +20,367 @@
 
 from zope.interface import implementedBy
 from zope.configuration.config import ConfigurationContext
-from zope.security.checker import getCheckerForInstancesOf
+from zope.proxy import removeAllProxies
 
-from zope.app.apidoc.utilities import \
-     getPythonPath, stx2html, getPermissionIds, getFunctionSignature, \
-     getPublicAttributes, getInterfaceForAttribute
-
-def _getTypeLink(type):
-    path = getPythonPath(type)
-    if path.startswith('__builtin__'):
-        return None
-    return path
+from zope.app import zapi
+from zope.app.apidoc.utilities import getPythonPath, stx2html, columnize
+from zope.app.apidoc.utilities import getPermissionIds, getFunctionSignature
+from zope.app.apidoc.utilities import getPublicAttributes
+from zope.app.apidoc.utilities import getInterfaceForAttribute
+from zope.app.apidoc.classmodule import Module, classRegistry
+from zope.app.apidoc.interfaces import IDocumentationModule
+
+class Menu(object):
+    """Menu for the Class Documentation Module.
+
+    The menu allows for looking for classes by partial names. See
+    'findClasses()' for the simple search implementation.
+    """
+
+    def findClasses(self):
+        """Find the classes that match a partial path.
+
+        Examples::
+
+          >>> import pprint
+          >>> from zope.app.apidoc.classmodule import Class
+          >>> cm = zapi.getUtility(None, IDocumentationModule, 'Class')
+          >>> mod = cm['zope']['app']['apidoc']['classmodule']['browser']
+
+          Setup a couple of classes and register them.
+
+          >>> class Foo(object):
+          ...     pass
+          >>> mod._Module__children['Foo'] = Class(mod, 'Foo', Foo)
+          >>> class Foo2(object):
+          ...     pass
+          >>> mod._Module__children['Foo2'] = Class(mod, 'Foo2', Foo2)
+          >>> class Blah(object):
+          ...     pass
+          >>> mod._Module__children['Blah'] = Class(mod, 'Blah', Blah)
+
+          Setup the view.
+
+          >>> from zope.publisher.browser import TestRequest
+          >>> menu = Menu()
+          >>> menu.context = None
+
+          Testing the method with various inputs.
+          
+          >>> menu.request = TestRequest(form={'path': 'Foo'})
+          >>> info = menu.findClasses()
+          >>> info.sort()
+          >>> pprint.pprint(info)
+          [{'path': 'zope.app.apidoc.classmodule.browser.Foo',
+            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo'},
+           {'path': 'zope.app.apidoc.classmodule.browser.Foo2',
+            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo2'}]
+
+          >>> menu.request = TestRequest(form={'path': 'o2'})
+          >>> info = menu.findClasses()
+          >>> info.sort()
+          >>> pprint.pprint(info)
+          [{'path': 'zope.app.apidoc.classmodule.browser.Foo2',
+            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Foo2'}]
+
+          >>> menu.request = TestRequest(form={'path': 'Blah'})
+          >>> info = menu.findClasses()
+          >>> info.sort()
+          >>> pprint.pprint(info)
+          [{'path': 'zope.app.apidoc.classmodule.browser.Blah',
+            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/browser/Blah'}]
+        """
+        path = self.request.get('path', None)
+        if path is None:
+            return []
+        classModule = zapi.getUtility(None, IDocumentationModule, "Class")
+        results = []
+        for p in classRegistry.keys():
+            if p.find(path) >= 0:
+                klass = zapi.traverse(classModule, p.replace('.', '/'))
+                results.append(
+                    {'path': p,
+                     'url': zapi.getView(klass, 'absolute_url', self.request)()
+                     })
+        return results
     
 class ClassDetails(object):
-    """Represents the details of the class"""
-
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-        self.path = request['path']
-        self.klass = ConfigurationContext().resolve(self.path)
-        self.checker = getCheckerForInstancesOf(self.klass)
-        self.interfaces = list(implementedBy(self.klass))
-        all_ifaces = {}
-        for iface in self.interfaces:
-            all_ifaces[iface] = 1
-            for base in iface.getBases():
-                all_ifaces[base] = 1
-        self._all_ifaces = all_ifaces.keys()
+    """Represents the details of the class."""
 
     def getBases(self):
-        """Get all bases of this class"""
-        return [getPythonPath(base) for base in self.klass.__bases__]
+        """Get all bases of this class.
+
+        Example::
+
+          The class we are using for this view is
+          zope.app.apidoc.classmodule.ClassModule.
+
+          >>> import pprint
+          >>> from tests import getClassDetailsView
+          >>> view = getClassDetailsView()
+
+          >>> pprint.pprint(view.getBases())
+          [{'path': 'zope.app.apidoc.classmodule.Module',
+            'url': 'http://127.0.0.1/zope/app/apidoc/classmodule/Module'}]
+        """
+        info = []
+        classModule = zapi.getUtility(None, IDocumentationModule, "Class")
+        for base in self.context.getBases():
+            path = getPythonPath(base)
+            klass = zapi.traverse(classModule, path.replace('.', '/'))
+            info.append(
+                {'path': path,
+                 'url': zapi.getView(klass, 'absolute_url',
+                                     self.request)()})
+        return info
+
+
+    def getBaseURL(self):
+        """Return the URL for the API Documentation Tool.
+
+        Example::
+
+          >>> from tests import getClassDetailsView
+          >>> view = getClassDetailsView()
+
+          Note that the following output is a bit different than usual, since
+          we have not setup all path elements.
+          
+          >>> view.getBaseURL()
+          'http://127.0.0.1'
+        """
+        m = zapi.getUtility(None, IDocumentationModule, "Class")
+        return zapi.getView(zapi.getParent(m), 'absolute_url', self.request)()
+
 
     def getInterfaces(self):
-        """Get all interfaces of this class."""
-        return [getPythonPath(iface) for iface in self.interfaces]
+        """Get all implemented interfaces (as paths) of this class.
+
+        Example::
+
+          The class we are using for this view is
+          zope.app.apidoc.classmodule.ClassModule.
+
+          >>> import pprint
+          >>> from tests import getClassDetailsView
+          >>> view = getClassDetailsView()
+
+          >>> pprint.pprint(view.getInterfaces())
+          ['zope.app.apidoc.interfaces.IDocumentationModule',
+           'zope.app.location.interfaces.ILocation',
+           'zope.app.apidoc.classmodule.IModuleDocumentation',
+           'zope.app.container.interfaces.IReadContainer']
+        """
+        return map(getPythonPath, self.context.getInterfaces())
+
 
     def getAttributes(self):
-        """Get all attributes of this class."""
+        """Get all attributes of this class.
+
+        Example::
+
+          The class we are using for this view is
+          zope.app.apidoc.classmodule.ClassModule.
+
+          >>> import pprint
+          >>> from tests import getClassDetailsView
+          >>> view = getClassDetailsView()
+
+          >>> attr = view.getAttributes()[2]
+          >>> items = attr.items()
+          >>> items.sort()
+          >>> pprint.pprint(items)
+          [('interface', 'zope.app.apidoc.interfaces.IDocumentationModule'),
+           ('name', 'title'),
+           ('read_perm', None),
+           ('type', 'str'),
+           ('type_link', '__builtin__.str'),
+           ('value', "'Classes'"),
+           ('write_perm', None)]
+        """
         attrs = []
-        for name in getPublicAttributes(self.klass):
-            attr = getattr(self.klass, name)
-            if not inspect.ismethod(attr):
-                entry = {'name': name,
-                         'value': attr.__repr__(),
-                         'type': type(attr).__name__,
-                         'type_link': _getTypeLink(type(attr)),
-                         'interface': getInterfaceForAttribute(
-                                         name, self._all_ifaces)}
-                entry.update(getPermissionIds(name, self.checker))
-                attrs.append(entry)
+        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(self.context.getSecurityChecker())
+            entry.update(getPermissionIds(name, checker))
+            attrs.append(entry)
         return attrs
 
+
     def getMethods(self):
-        """Get all methods of this class."""
+        """Get all methods of this class.
+
+        Example::
+
+          The class we are using for this view is
+          zope.app.apidoc.classmodule.ClassModule.
+
+          >>> import pprint
+          >>> from tests import getClassDetailsView
+          >>> view = getClassDetailsView()
+
+          >>> methods = view.getMethods()
+          >>> methods.sort()
+          >>> items = [m.items() for m in methods[:2]]
+          >>> items.sort()
+          >>> pprint.pprint(items)
+          [[('write_perm', None),
+            ('read_perm', None),
+            ('name', 'keys'),
+            ('signature', '()'),
+            ('interface', 'zope.interface.common.mapping.IEnumerableMapping'),
+            ('doc', '')],
+           [('write_perm', None),
+            ('read_perm', None),
+            ('name', 'values'),
+            ('signature', '()'),
+            ('interface', 'zope.interface.common.mapping.IEnumerableMapping'),
+            ('doc', '')]]
+        """
         methods = []
-        for name in getPublicAttributes(self.klass):
-            attr = getattr(self.klass, name)
+        for name, attr, iface in self.context.getMethods():
             if inspect.ismethod(attr):
                 entry = {'name': name,
                          'signature': getFunctionSignature(attr),
                          'doc': stx2html(attr.__doc__ or ''),
-                         'interface': getInterfaceForAttribute(
-                                          name, self._all_ifaces)}
-                entry.update(getPermissionIds(name, self.checker))
+                         'interface': getPythonPath(iface)}
+                entry.update(getPermissionIds(
+                    name, self.context.getSecurityChecker()))
                 methods.append(entry)
         return methods
 
+
     def getDoc(self):
-        """Get the doc string of the class."""
-        return stx2html(self.klass.__doc__ or '')
+        """Get the doc string of the class STX formatted.
+
+        Example::
+
+          The class we are using for this view is
+          zope.app.apidoc.classmodule.ClassModule.
+
+          >>> import pprint
+          >>> from tests import getClassDetailsView
+          >>> view = getClassDetailsView()
+
+          >>> print view.getDoc()[:59]
+          <h1>Represent the Documentation of any possible class.</h1>
+        """
+        return stx2html(self.context.getDocString() or '')
 
 
 class ModuleDetails(object):
-    """ """
+    """Represents the details of the module."""
+
+    def getDoc(self):
+        """Get the doc string of the module STX formatted.
+
+        Example::
 
-    def __init__(self, context, request):
-        self.context = context
-        self.request = request
-        self.module_name = request.get('module', '')
-        if self.module_name:
-            self.module = __import__(self.module_name, {}, {}, ('*',))
-
-    def isRoot(self):
-        return self.module_name == ''
-
-    def getEntries(self):
-        if self.isRoot():
-            return [{'name': name, 'path': name, 'module': True}
-                    for name in self.context.rootModules]
-        entries = []
-        # Detect packages
-        if self.module.__file__.endswith('__init__.pyc'):
-            dir = os.path.split(self.module.__file__)[0]
-            for file in os.listdir(dir):
-                path = os.path.join(dir, file)
-                if os.path.isdir(path) and '__init__.py' in os.listdir(path):
-                    entries.append(
-                        {'name': file,
-                         'path': self.module.__name__ + '.' + file,
-                         'module': True})
-                elif os.path.isfile(path) and file.endswith('.py') and \
-                         not file.startswith('__init__'):
-                    entries.append(
-                        {'name': file[:-3],
-                         'path': self.module.__name__ + '.' + file[:-3],
-                         'module': True})
-
-        for name in self.module.__dict__.keys():
-            attr = getattr(self.module, name)
-            if `attr`.startswith('<class'):
-                entries.append(
-                    {'name': name,
-                     'path': attr.__module__ + '.' + attr.__name__,
-                     'module': False})
-                
+          The class we are using for this view is zope.app.apidoc.classmodule.
+
+          >>> from tests import getModuleDetailsView
+          >>> view = getModuleDetailsView()
+
+          >>> print view.getDoc().strip()
+          <p>Class Documentation Module</p>
+          <p>This module is able to take a dotted name of any class and display
+          documentation for it. </p>
+        """
+        text = self.context.getDocString()
+        if text is None:
+            return None
+        lines = text.strip().split('\n')
+        # Get rid of possible CVS id.
+        lines = [line for line in lines if not line.startswith('$Id')]
+        return stx2html('\n'.join(lines))
+
+    def getEntries(self, columns=True):
+        """Return info objects for all modules and classes in this module.
+
+        Example::
+
+          The class we are using for this view is zope.app.apidoc.classmodule.
+
+          >>> import pprint
+          >>> from tests import getModuleDetailsView
+          >>> view = getModuleDetailsView()
+
+          >>> entries = [e.items() for e in view.getEntries(False)]
+          >>> entries.sort()
+          >>> pprint.pprint(entries[:2])
+          [[('url', 'http://127.0.0.1/zope/app/apidoc/classmodule/Class'),
+            ('name', 'Class'),
+            ('module', False)],
+           [('url', 'http://127.0.0.1/zope/app/apidoc/classmodule/ClassModule'),
+            ('name', 'ClassModule'),
+            ('module', False)]]
+        """
+        entries = [{'name': name,
+                    'url': zapi.getView(obj, 'absolute_url', self.request)(),
+                    'module': type(removeAllProxies(obj)) is Module}
+                   for name, obj in self.context.items()]
         entries.sort(lambda x, y: cmp(x['name'], y['name']))
+        if columns:
+            entries = columnize(entries)
         return entries
 
-    def getEntriesInColumns(self, columns=3):
-        entries = self.getEntries()
-        per_col = len(entries)/3 + 1
-        columns = []
-        col = []
-        in_col = 0
-        for entry in entries:
-            if in_col < per_col:
-                col.append(entry)
-                in_col += 1
-            else:
-                columns.append(col)
-                col = [entry]
-                in_col = 1
-        if col:
-            columns.append(col)
-        return columns
-            
-        
     def getBreadCrumbs(self):
-        names = self.module_name.split('.') 
-        mods = []
-        for i in xrange(len(names)):
-            mods.append(
-                {'name': names[i],
-                 'path': '.'.join(names[:i+1])}
+        """Create breadcrumbs for the module path.
+
+        We cannot reuse the the system's bread crumbs, since they go all the
+        way up to the root, but we just want to go to the root module.
+
+        Example::
+
+          >>> import pprint
+          >>> from tests import getModuleDetailsView
+          >>> view = getModuleDetailsView()
+
+          >>> crumbs = [crumb.items() for crumb in view.getBreadCrumbs()]
+          >>> crumbs.sort()
+          >>> pprint.pprint(crumbs)
+          [[('url', 'http://127.0.0.1'), ('name', '[top]')],
+           [('url', 'http://127.0.0.1/zope'), ('name', 'zope')],
+           [('url', 'http://127.0.0.1/zope/app'), ('name', 'app')],
+           [('url', 'http://127.0.0.1/zope/app/apidoc'), ('name', 'apidoc')],
+           [('url', 'http://127.0.0.1/zope/app/apidoc/classmodule'),
+            ('name', 'classmodule')]]
+        """
+        names = self.context.getPath().split('.') 
+        crumbs = []
+        module = self.context
+        while type(removeAllProxies(module)) is Module:
+            crumbs.append(
+                {'name': zapi.name(module),
+                 'url': zapi.getView(module, 'absolute_url', self.request)()}
                 )
-        return mods
+            module = zapi.getParent(module)
+
+        crumbs.append(
+            {'name': '[top]',
+             'url': zapi.getView(module, 'absolute_url', self.request)()} )
+
+        crumbs.reverse()
+        return crumbs
+
+
+def _getTypePath(type):
+    """Return the path of the type.
+
+    Here some examples::
+
+      >>> class Foo(object):
+      ...     pass
+
+      >>> _getTypePath(type(Foo()))
+      'zope.app.apidoc.classmodule.browser.Foo'
+
+      >>> _getTypePath(type(3))
+      '__builtin__.int'
+    """
+    path = getPythonPath(type)
+    return path




More information about the Zope3-Checkins mailing list