[Zope3-checkins] CVS: Zope3/src/zope/products/apidoc/ifacemodule - __init__.py:1.1 browser.py:1.1 configure.zcml:1.1 index.pt:1.1 menu.pt:1.1 menu.py:1.1 tests.py:1.1

Stephan Richter srichter at cosmos.phy.tufts.edu
Thu Jan 29 12:51:18 EST 2004


Update of /cvs-repository/Zope3/src/zope/products/apidoc/ifacemodule
In directory cvs.zope.org:/tmp/cvs-serv11915/apidoc/ifacemodule

Added Files:
	__init__.py browser.py configure.zcml index.pt menu.pt menu.py 
	tests.py 
Log Message:
Here comes the new Zope 3 API Documentation tool. You can access it via

  http://localhost:8080/++apidoc++/

There is really not much more to say here. Check it out and let me know what
you think.


=== Added File Zope3/src/zope/products/apidoc/ifacemodule/__init__.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface Documentation Module

The interface documentation module retrieves its information from the
interface service. Therefore, currently there are no unregsitered interfaces
listed in the documentation. This might be good, since unregistered interfaces
are usually private and not of interest to a general developer.

$Id: __init__.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
from zope.app import zapi
from zope.interface import implements
from zope.products.apidoc.interfaces import IDocumentationModule
from zope.products.apidoc.utilities import ReadContainerBase
from zope.app.location import LocationProxy


class IInterfaceModule(IDocumentationModule):
    """Interface API Documentation Module

    This is a marker interface, so that we can write adapters for objects
    implementing this interface.
    """

class InterfaceModule(ReadContainerBase):
    r"""Represent the Documentation of all Interfaces.

    This documentation is implemented using a simply 'IReadContainer'. The
    items of the container are all the interfaces listed in the closest
    interface service and above.

    Demonstration::

      >>> from zope.products.apidoc.ifacemodule import tests
      >>> tests.setUp()

      >>> module = InterfaceModule()
      >>> module.get('IInterfaceModule').getName()
      'IInterfaceModule'

      >>> id = 'zope.products.apidoc.interfaces.IDocumentationModule'
      >>> module.get(id).getName()
      'IDocumentationModule'

      Here we find an interface that is not in the interface service, but
      exists.

      >>> module.get('zope.app.interfaces.content.IContentType').getName()
      'IContentType'

      >>> print '\n'.join([id for id, iface in module.items()])
      IInterfaceModule
      zope.products.apidoc.interfaces.IDocumentationModule
      
      >>> tests.tearDown()
    """

    implements(IInterfaceModule)

    # See zope.products.apidoc.interfaces.IDocumentationModule
    title = 'Interfaces'

    # See zope.products.apidoc.interfaces.IDocumentationModule
    description = """
    All used and important interfaces are registered through the interface
    service. While it would be possible to just list all attributes, it is
    hard on the user to read such an overfull list. Therefore, interfaces that
    have partial common module paths are bound together.

    The documentation of an interface also provides a wide variety of
    information, including of course the declared attributes/fields and
    methods, but also available adapters, services and utilities that provide
    this interface.
    """

    def get(self, key, default=None):
        """See zope.app.interfaces.container.IReadContainer"""
        service = zapi.getService(self, 'Interfaces')
        iface = service.queryInterface(key, default)
        if iface is default: 
            # Yeah, we find more items than we claim to have! This way we can
            # handle all interfaces using this module. :-)
            parts = key.split('.')
            try:
                mod = __import__('.'.join(parts[:-1]), {}, {}, ('*',))
            except ImportError:
                iface = default
            else:
                iface = getattr(mod, parts[-1], default)

        if not iface is default:
            iface = LocationProxy(iface, self, key)

        return iface

    def items(self):
        """See zope.app.interfaces.container.IReadContainer"""
        service = zapi.getService(self, 'Interfaces')
        items = list(service.items())
        items.sort()
        items = [(i[0], LocationProxy(i[1], self, i[0])) for i in items]
        return items


=== Added File Zope3/src/zope/products/apidoc/ifacemodule/browser.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface Details View

$Id: browser.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
from zope.app import zapi
from zope.component import ComponentLookupError
from zope.interface.declarations import providedBy, directlyProvidedBy
from zope.interface.interfaces import IMethod, IAttribute, IInterface 
from zope.products.apidoc.utilities import getPythonPath, stx2html
from zope.proxy import removeAllProxies
from zope.schema.interfaces import IField

def _get(iface, type):
    """Return a dictionary containing all the Fields in a schema."""
    iface = removeAllProxies(iface)
    items = {}
    for name in iface:
        attr = iface[name]
        if type.isImplementedBy(attr):
            items[name] = attr
    return items

def _getInOrder(iface, type,
                _itemsorter=lambda x, y: cmp(x[1].order, y[1].order)):
    """Return a list of (name, value) tuples in native schema order."""
    items = _get(iface, type).items()
    items.sort(_itemsorter)
    return items

def _getFieldInterface(field):
    """Return PT-friendly dict about the field's interface."""
    field = removeAllProxies(field)
    # This is bad, but due to bootstrapping, directlyProvidedBy does
    # not work 
    name = field.__class__.__name__
    ifaces = list(providedBy(field))
    for iface in ifaces:
        if iface.getName() == 'I' + name:
            return {'name': iface.getName(), 'id': getPythonPath(iface)}
    # Giving up...
    return {'name': ifaces[0].getName(), 'id': getPythonPath(ifaces[0])}

def _getRequired(field):
    """Return a string representation of whether the field is required."""
    if field.required:
        return 'required'
    else:
        return 'optional'


class InterfaceDetails(object):
    """View class for an Interface."""

    def __init__(self, context, request):
        self.context = context
        self.request = request

    def getId(self):
        """Return the id of the field as it is defined in the interface
        service."""
        return zapi.name(self.context)

    def getDoc(self):
        """Return the main documentation string of the interface."""
        return stx2html(self.context.getDoc())

    def getBases(self):
        """Get all bases of this class"""
        return [getPythonPath(base) for base in self.context.__bases__]

    def getTypes(self):
        """Return a list of interface types that are specified for this
        interface.

        Note that you should only expect one type at a time."""
        context = removeAllProxies(self.context)
        types = list(providedBy(context))
        types.remove(IInterface)
        return [{'name': type.getName(),
                 'path': getPythonPath(type)}
                for type in types]
    
    def getAttributes(self):
        """Return a list of attributes in the order they were specified."""
        iface = removeAllProxies(self.context)
        attrs = []
        for name in iface:
            attr = iface[name]
            if not IMethod.isImplementedBy(attr) and \
               not IField.isImplementedBy(attr):
                attrs.append(attr)
        return [{'name': attr.getName(),
                 'doc': stx2html(attr.getDoc() or '', 3)}
                for attr in attrs]

    def getMethods(self):
        """Return a list of methods in the order they were specified."""
        methods = []
        return [{'name': method.getName(),
                 'signature': method.getSignatureString(),
                 'doc': stx2html(method.getDoc() or '', 3)}
                for method in _get(self.context, IMethod).values()]
            
    def getFields(self):
        """Return a list of fields in the order they were specified."""
        fields = map(lambda x: x[1], _getInOrder(self.context, IField))
        return [{'name': field.getName(),
                 'iface': _getFieldInterface(field),
                 'required': _getRequired(field),
                 'default': field.default.__repr__,
                 'description': field.description
                 }
                for field in fields]

    def getRequiredAdapters(self):
        """Get adapters where this interface is required."""
        service = zapi.getService(self.context, 'Adapters')
        context = removeAllProxies(self.context)
        adapters = []
        for adapter in service.getRegisteredMatching(required=context):
            adapters.append({
                'provided': getPythonPath(adapter[1]),
                'required': [getPythonPath(iface) for iface in adapter[2]],
                'name': adapter[3],
                'factory': getPythonPath(adapter[4][0])
                })
        return adapters
        
    def getProvidedAdapters(self):
        """Get adapters where this interface is provided."""
        service = zapi.getService(self.context, 'Adapters')
        context = removeAllProxies(self.context)
        adapters = []
        for adapter in service.getRegisteredMatching(provided=context):
            adapters.append({
                'required': [getPythonPath(iface)
                             for iface in adapter[2]+(adapter[0],)],
                'name': adapter[3],
                'factory': getPythonPath(adapter[4][0])
                })
        return adapters

    def getFactories(self):
        """Return the factories, who will provide objects implementing this
        interface."""
        service = zapi.getService(self.context, 'Factories')
        try:
            factories = service.getFactoriesFor(removeAllProxies(self.context))
        except ComponentLookupError:
            return []
        return [{'name': n,
                 'factory': f,
                 'title': service.getFactoryInfo(n).title
                 } for n, f in factories]

    def getUtilities(self):
        """Return all utilities that provide this interface."""
        service = zapi.getService(self.context, 'Utilities')
        utils = service.getUtilitiesFor(removeAllProxies(self.context))
        return [{'name': util[0],
                 'path': getPythonPath(util[1].__class__)} for util in utils]

    def getServices(self):
        """Return all services (at most one)  that provide this interface."""
        iface = removeAllProxies(self.context)
        service = zapi.getService(self.context, 'Services')
        services = service.getServiceDefinitions()
        services = filter(lambda x: x[1] is iface, services)
        return [ser[0] for ser in services]


=== Added File Zope3/src/zope/products/apidoc/ifacemodule/configure.zcml ===
<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser">

  <class class=".InterfaceModule">
    <allow interface=".IInterfaceModule" />
    <allow interface="zope.app.interfaces.container.IReadContainer" />
  </class>

  <!-- Register the module with the API Documentation System -->

  <utility
      provides="zope.products.apidoc.interfaces.IDocumentationModule"
      factory=".InterfaceModule"
      name="Interface" />

  <!-- The name for the interface content cannot be 'index.html', since the 
       introspector uses this name already. -->

  <browser:page
    for="zope.interface.interfaces.IInterface"
    permission="zope.View"
    class=".browser.InterfaceDetails"
    name="apiindex.html"
    template="index.pt" />

  <!-- Interface Documentation Module Menu -->

  <adapter
      provides="zope.products.statictree.interfaces.IChildObjects"
      for=".IInterfaceModule"
      factory=".menu.InterfaceModuleChildObjects" />

  <browser:page
      for=".InterfaceModule"
      permission="zope.View"
      class=".menu.Menu"
      name="menu.html"
      template="menu.pt" />

</configure>


=== Added File Zope3/src/zope/products/apidoc/ifacemodule/index.pt ===
<html metal:use-macro="views/apidoc_macros/details">
<body metal:fill-slot="contents">

  <h1 class="details-header" tal:content="view/getId">
    zope.app.interfaces.IInterface
  </h1>

  <h3 tal:condition="view/getTypes">
    Type: 
    <tal:omit-tag tal:repeat="type view/getTypes" >
      <a href=""
          tal:attributes="href string:../${type/path}/apiindex.html"
          tal:content="type/name"/><tal:block 
          condition="not:repeat/type/end">, </tal:block>
    </tal:omit-tag>
  </h3>


  <div class="indent">
    <div class="documentation" tal:content="structure view/getDoc">
      Here is the doc string
    </div>
  </div>


  <h2 class="details-section">Bases</h2>

  <div class="indent"
         tal:define="bases view/getBases">
    
    <ul class="attr-list" tal:condition="bases">
      <li tal:repeat="base bases">
        <a href=""
           tal:attributes="href string:../$base/apiindex.html"
           tal:content="base" />
      </li>
    </ul>
    
    <p tal:condition="not: bases">
      <em>There are no base classes.</em>
    </p>
    
  </div>


  <h2 class="details-section">Attributes/Fields</h2>

  <div class="indent"
      tal:define="attributes view/getAttributes;
                  fields view/getFields">

  <ul class="attr-list" 
      tal:condition="python: attributes or fields">
  
    <li tal:repeat="attr attributes">
      <b><code tal:content="attr/name">attr</code></b> (Attribute)<br>
      <div class="inline-documentation" tal:content="structure attr/doc">
        attr desc
      </div>      
    </li>

    <li tal:repeat="field fields">
      <b><code tal:content="field/name">field</code></b>
      - <a href=""
           tal:attributes="href string:../${field/iface/id}/apiindex.html">
          <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>      
    </li>

  </ul>

  <p tal:condition="python: not (attributes or fields)">
    <em>There are no attributes or fields specified.</em>
  </p>

  </div>



  <h2 class="details-section">Methods</h2>

  <div class="indent">

  <ul class="attr-list" tal:condition="view/getMethods">
    <li tal:repeat="method view/getMethods">
      <b><code 
          tal:content="string:${method/name}${method/signature}" />
      </b><br>
      <div class="inline-documentation" tal:content="structure method/doc">
        method desc
      </div>      
    </li>
  </ul>

  <p tal:condition="not: view/getMethods">
    <em>There are no methods or fields specified.</em>
  </p>

  </div>



  <h2 class="details-section">Adapters</h2>
  
  <div class="indent" 
       tal:define="adapters view/getRequiredAdapters"
       tal:condition="adapters">

    <h3>Adapters where this interface is required:</h3>
    <ul class="attr-list">
      <li tal:repeat="adapter adapters">
        <b><code tal:content="adapter/factory" /></b>
        <span tal:condition="adapter/name">
          (name: <tal:block content="adapter/name" />)
        </span>
        <br />
        <i>provides:</i> 
          <a href=""
              tal:attributes="href string:../${adapter/provided}/apiindex.html"
              tal:content="adapter/provided" /><br />
        <tal:block condition="adapter/required">
        <i>also required:</i>
          <tal:block repeat="iface adapter/required">
            <a href=""
                tal:attributes="href string:../$iface/apiindex.html"
                tal:content="iface" /><tal:block 
                condition="not:repeat/iface/end">, </tal:block>
          </tal:block>
        </tal:block>
      </li>
    </ul>

  </div>

  <div class="indent" 
       tal:define="adapters view/getProvidedAdapters"
       tal:condition="adapters">

    <h3>Adapters that provide this interface:</h3>
    <ul>
      <li tal:repeat="adapter adapters">
        <b><code tal:content="adapter/factory" /></b>
        <span tal:condition="adapter/name">
          (name: <tal:block content="adapter/name" />)
        </span>
        <br />
        <i>requires:</i> 
          <tal:block repeat="iface adapter/required">
            <a href=""
                tal:condition="iface"
                tal:attributes="href string:../$iface/"
                tal:content="iface" /><tal:block 
                condition="not:repeat/iface/end">, </tal:block>
            <span tal:condition="not: iface">
              No interface required.
            </span>
          </tal:block>
      </li>
    </ul>

  </div>

  <p tal:condition="
     python: not (view.getRequiredAdapters() or view.getProvidedAdapters())">
    <em>There are no adapters registered for this interface.</em>
  </p>

  <div tal:define="factories view/getFactories;
                   utilities view/getUtilities;
                   services view/getServices"
       tal:condition="python: factories or utilities or services">

    <h2 class="details-section">Other Information</h2>
    
    <div class="indent">
    
      <tal:block condition="factories">
        <h3 class="details-section">Factories</h3>
        <div class="indent">
          <div class="small">
            A list of factories that create objects implement this interface.
          </div>
          <ul>
            <li tal:repeat="factory factories">
              <a href=""
                 tal:attributes="href string:../../Factory/${factory/name}/"
                 tal:content="factory/name" />
              <tal:block replace="string:(${factory/title})" 
                         condition="factory/title" />
            </li>
          </ul>
        </div>
      </tal:block>
    
      <tal:block condition="utilities">
        <h3 class="details-section">Utilities</h3>
        <div class="indent">
          <div class="small">
            A list of utilities that are are registered to provide this 
            interface.
          </div>
          <ul>
            <li tal:repeat="utility utilities">
              <a href=""
                 tal:attributes="href string:../../Utility/${utility/path}/"
                 tal:content="utility/path" />
              <tal:block replace="string: (name: ${utility/name})" 
                         condition="utility/name" />
            </li>
          </ul>
        </div>
      </tal:block>
    
      <tal:block condition="services">
        <h3 class="details-section">Service</h3>
        <div class="indent">
          <div class="small">
            A list of services (usually just one) that are are registered to 
            provide this interface.
          </div>
          <ul>
            <li tal:repeat="service services">
              <a href=""
                 tal:attributes="href string:../../Service/$service/"
                 tal:content="service" />
            </li>
          </ul>
        </div>
      </tal:block>
    
    </div>
  </div>

</body>
</html>

=== Added File Zope3/src/zope/products/apidoc/ifacemodule/menu.pt ===
<html metal:use-macro="views/apidoc_macros/menu">
<body>

  <p metal:fill-slot="post_menu" class="small">
    Note: These are only interfaces that are registered with the Interface
    Service. 
  </p>

</body>
</html>

=== Added File Zope3/src/zope/products/apidoc/ifacemodule/menu.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface Module Browser Menu (Tree)

A list of interfaces from the Interface Service is pretty much unmanagable and
is very confusing. Therefore it is nice to split the path of the interface, so
that we get a deeper tree with nodes having shorter, manageable names.

$Id: menu.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
from zope.app import zapi
from zope.interface import implements
from zope.app.interfaces.location import ILocation
from zope.app.location import LocationProxy
from zope.products.statictree.interfaces import IChildObjects
from zope.products.apidoc.ifacemodule import IInterfaceModule
from zope.products.apidoc.utilities import ReadContainerBase
from zope.proxy import removeAllProxies

class IModule(ILocation):
    """Represents some module

    Marker interface, so that we can register an adapter for it."""
    

class Module(ReadContainerBase):
    r"""Represents a Python module

    Examples: zope, zope.app, zope.app.interfaces

    However, we usually use it only for the last case.

    Usage::

      >>> from zope.products.apidoc.ifacemodule import tests
      >>> from zope.products.apidoc.ifacemodule import InterfaceModule
      >>> tests.setUp()

      >>> module = Module(InterfaceModule(), 'zope.products')
      >>> module.get('apidoc.interfaces.IDocumentationModule').getName()
      'IDocumentationModule'

      >>> module.get(
      ...     'zope.products.apidoc.interfaces.IDocumentationModule') is None
      True

      >>> print '\n'.join([id for id, iface in module.items()])
      zope.products.apidoc.interfaces.IDocumentationModule

      >>> tests.tearDown()
    """
    
    implements(IModule)

    def __init__(self, parent, name):
        self.__parent__ = parent
        self.__name__ = name

    def get(self, key, default=None):
        name = self.__name__ + '.' + key
        return self.__parent__.get(name, default)

    def items(self):
        parent = self.__parent__
        items = []
        for key in parent.keys():
            if key.startswith(self.__name__):
                items.append((key, LocationProxy(parent[key], self, key)))
        return items


class InterfaceModuleChildObjects:
    r"""Module Adapter for Static Tree

    This adapter is used when building a static tree for the browser.

    Functionality::

      >>> from zope.products.apidoc.ifacemodule import tests
      >>> from zope.products.apidoc.ifacemodule import InterfaceModule
      >>> tests.setUp()

      >>> module = InterfaceModule()
      >>> module = tests.rootLocation(module, 'Interface')
      >>> adapter = InterfaceModuleChildObjects(module)
      >>> adapter.hasChildren()
      True

      >>> print '\n'.join([c.__name__ for c in adapter.getChildObjects()])
      IInterfaceModule
      zope.products.apidoc.interfaces

      >>> tests.tearDown()      
    """

    implements(IChildObjects)
    __used_for__ = IInterfaceModule

    def __init__(self, context):
        self.context = context

    def hasChildren(self):
        """See zope.products.statictree.interfaces.IChildObject"""
        return bool(len(self.context))

    def getChildObjects(self):
        """See zope.products.statictree.interfaces.IChildObject"""
        objects = {}
        names = removeAllProxies(self.context.keys())
        names.sort()
        for name in names:
            # Split these long names and make part of the module path separate
            # entries. Currently we only split by the term '.interfaces', but
            # a more sophisticated algorithm could be developed. 
            iface_loc = name.find('.interfaces')
            if iface_loc == -1:
                objects[name] = LocationProxy(self.context[name],
                                              self.context, name)
            else:
                module_name = name[:iface_loc+11]
                objects[module_name] = Module(self.context, module_name)
        items = objects.items()
        items.sort()
        return [x[1] for x in items]


class Menu(object):
    """Menu View Helper Class

    A class that helps building the menu. The menu_macros expects the menu view
    class to have the getMenuTitle(node) and getMenuLink(node) methods
    implemented. 'node' is a 'zope.products.statictree.node.Node' instance.
    """

    def getMenuTitle(self, node):
        """Return the title of the node that is displayed in the menu."""
        if isinstance(removeAllProxies(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):
            return None

        return './' + zapi.name(node.context) + '/apiindex.html'


=== Added File Zope3/src/zope/products/apidoc/ifacemodule/tests.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests for the Interface Documentation Module

$Id: tests.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
import unittest
from zope.app import zapi
from zope.app.interfaces.traversing import IContainmentRoot
from zope.app.location import LocationProxy
from zope.app.tests import placelesssetup
from zope.interface import implements
from zope.products.apidoc.ifacemodule import IInterfaceModule
from zope.products.apidoc.interfaces import IDocumentationModule
from zope.testing.doctestunit import DocTestSuite


def setUp():
    placelesssetup.setUp()
    service = zapi.getService(None, 'Interfaces')
    service.provideInterface(None, IDocumentationModule)
    service.provideInterface('IInterfaceModule', IInterfaceModule)

def tearDown():
    placelesssetup.tearDown()


class Root:
    implements(IContainmentRoot)

    __parent__ = None
    __name__ = ''

def rootLocation(obj, name):
    return LocationProxy(obj, Root(), name)
    
def test_suite():
    return unittest.TestSuite((
        DocTestSuite('zope.products.apidoc.ifacemodule'),
        DocTestSuite('zope.products.apidoc.ifacemodule.menu'),
        ))

if __name__ == '__main__':
    unittest.main()




More information about the Zope3-Checkins mailing list