Metadata-Version: 1.0
Name: zope.app.apidoc
Version: 3.7.5
Summary: API Documentation and Component Inspection for Zope 3
Home-page: http://pypi.python.org/pypi/zope.app.apidoc
Author: Zope Corporation and Contributors
Author-email: zope-dev@zope.org
License: ZPL 2.1
Description: This Zope 3 package provides fully dynamic API documentation of Zope 3 and
        registered add-on components. The package is very extensible and can be easily
        extended by implementing new modules.
        
        The static version of the full Zope tree is available at:
        
        http://apidoc.zope.org
        
        
        .. contents::
        
        ========================
        Zope 3 API Documentation
        ========================
        
        This Zope 3 package provides fully dynamic API documentation of Zope 3 and
        registered add-on components. The package is very extensible and can be easily
        extended by implementing new modules.
        
        Besides being an application, the API doctool also provides several public
        APIs to extract information from various objects used by Zope 3.
        
         * utilities -- Miscellaneous classes and functions that aid all documentation
           modules. They are broadly usable.
        
         * interface -- This module contains functions to inspect interfaces and
           schemas.
        
         * component -- This modules provides utility functions to lookup components
           given an interface.
        
         * presentation -- Presentation components are generally more complex than
           others, so a separate utilities module is provided to inspect views.
        
         * classregistry -- Here a simple dictionary-based registry for all known
           classes is provided. It allows us to search in classes.
        
        
        Using the API Dcoumentation
        ---------------------------
        
        The `APIDocumentation` class provides access to all available documentation
        modules. Documentation modules are utilities providing `IDocumentationModule`:
        
        
          >>> from zope.app.testing import ztapi
          >>> from zope.app.apidoc.interfaces import IDocumentationModule
          >>> from zope.app.apidoc.ifacemodule.ifacemodule import InterfaceModule
          >>> from zope.app.apidoc.zcmlmodule import ZCMLModule
        
          >>> ztapi.provideUtility(IDocumentationModule, InterfaceModule(),
          ...                      'Interface')
          >>> ztapi.provideUtility(IDocumentationModule, ZCMLModule(), 'ZCML')
        
        Now we can instantiate the class (which is usually done when traversing
        '++apidoc++') and get a list of available modules:
        
          >>> from zope.app.apidoc.apidoc import APIDocumentation
          >>> doc = APIDocumentation(None, '++apidoc++')
        
          >>> modules =  doc.keys()
          >>> modules.sort()
          >>> modules
          [u'Interface', u'ZCML']
        
          >>> doc['ZCML'] #doctest:+ELLIPSIS
          <zope.app.apidoc.zcmlmodule.ZCMLModule object at ...>
        
        
        Developing a Module
        -------------------
        
        1. Implement a class that realizes the `IDocumentationModule`
           interface.
        
        2. Register this class as a utility using something like this::
        
             <utility
                 provides="zope.app.apidoc.interfaces.IDocumentationModule"
                 factory=".examplemodule.ExampleModule"
                 name="Example" />
        
        3. Take care of security by allowing at least `IDocumentationModule`::
        
             <class class=".ExampleModule">
               <allow interface="zope.app.apidoc.interfaces.IDocumentationModule" />
             </class>
        
        4. Provide a browser view called ``menu.html``.
        
        5. Provide another view, usually ``index.html``, that can show the
           details for the various menu items.
        
        Note:  There are several modules that come with the product. Just look
        in them for some guidance.
        
        
        New Static APIDOC-Version
        -------------------------
        
        An alternative APIDOC-Version is available through ++apidoc++/static.html 
        Find and Tree is implemented in Javascript. So it should be possible to do a
        "wget" - Offline-Version of APIDOC
        
        
        
        
        
        ==============================
        Component Inspection Utilities
        ==============================
        
        Once you have an interface, you really want to discover on how this interface
        interacts with other components in Zope 3. The functions in
        
          >>> from zope.app.apidoc import component
        
        provide you with utilities to make those discoveries. The functions are
        explained in detail in this document. Before we start though, we have to have
        some interfaces to work with:
        
          >>> from zope.interface import Interface
          >>> class IFoo(Interface):
          ...     pass
        
          >>> class IBar(Interface):
          ...     pass
        
          >>> class IFooBar(IFoo, IBar):
          ...     pass
        
          >>> class IResult(Interface):
          ...     pass
        
          >>> class ISpecialResult(IResult):
          ...     pass
        
        
        `getRequiredAdapters(iface, withViews=False)`
        ---------------------------------------------
        
        This function returns adapter registrations for adapters that require the
        specified interface. So let's create some adapter registrations:
        
          >>> from zope.publisher.interfaces import IRequest
          >>> from zope.app.testing import ztapi
          >>> ztapi.provideAdapter((IFoo,), IResult, None)
          >>> ztapi.provideAdapter((IFoo, IBar), ISpecialResult, None)
          >>> ztapi.provideAdapter((IFoo, IRequest), ISpecialResult, None)
          >>> ztapi.subscribe((IFoo,), None, 'stubFactory')
        
          >>> regs = list(component.getRequiredAdapters(IFoo))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo], IResult, '', None, u''),
           HandlerRegistration(<BaseGlobalComponents base>,
                               [IFoo], u'', 'stubFactory', u'')]
        
        Note how the adapter requiring an `IRequest` at the end of the required
        interfaces is neglected. This is because it is recognized as a view and views
        are not returned by default. But you can simply turn this flag on:
        
          >>> regs = list(component.getRequiredAdapters(IFoo, withViews=True))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IRequest], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo], IResult, '', None, u''),
           HandlerRegistration(<BaseGlobalComponents base>,
                               [IFoo], u'', 'stubFactory', u'')]
        
        The function will also pick up registrations that have required interfaces the
        specified interface extends:
        
          >>> regs = list(component.getRequiredAdapters(IFoo))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo], IResult, '', None, u''),
           HandlerRegistration(<BaseGlobalComponents base>,
                               [IFoo], u'', 'stubFactory', u'')]
        
        And all of the required interfaces are considered, of course:
        
          >>> regs = list(component.getRequiredAdapters(IBar))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u'')]
        
        
        `getProvidedAdapters(iface, withViews=False)`
        ---------------------------------------------
        
        Of course, we are also interested in the adapters that provide a certain
        interface. This function returns those adapter registrations, again ignoring
        views by default.
        
          >>> regs = list(component.getProvidedAdapters(ISpecialResult))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u'')]
        
        And by specifying the `withView` flag, we get views as well:
        
          >>> regs = list(component.getProvidedAdapters(ISpecialResult, withViews=True))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IRequest], ISpecialResult, '', None, u'')]
        
        We can of course also ask for adapters specifying `IResult`:
        
          >>> regs = list(component.getProvidedAdapters(IResult, withViews=True))
          >>> regs.sort()
          >>> regs
          [AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IBar], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IRequest], ISpecialResult, '', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo], IResult, '', None, u'')]
        
        
        `getClasses(iface)`
        -------------------
        
        This package comes with a little tool called the class registry
        (see `classregistry.txt`). It provides a dictionary of all classes in the
        visible packages. This function utilizes the registry to retrieve all classes
        that implement the specified interface.
        
        Let's start by creating and registering some classes:
        
          >>> from zope.interface import implements
          >>> from zope.app.apidoc.classregistry import classRegistry
        
          >>> class MyFoo(object):
          ...     implements(IFoo)
          >>> classRegistry['MyFoo'] = MyFoo
        
          >>> class MyBar(object):
          ...     implements(IBar)
          >>> classRegistry['MyBar'] = MyBar
        
          >>> class MyFooBar(object):
          ...     implements(IFooBar)
          >>> classRegistry['MyFooBar'] = MyFooBar
        
        Let's now see whether what results we get:
        
          >>> classes = component.getClasses(IFooBar)
          >>> classes.sort()
          >>> classes
          [('MyFooBar', <class 'zope.app.apidoc.doctest.MyFooBar'>)]
        
          >>> classes = component.getClasses(IFoo)
          >>> classes.sort()
          >>> classes
          [('MyFoo', <class 'zope.app.apidoc.doctest.MyFoo'>),
           ('MyFooBar', <class 'zope.app.apidoc.doctest.MyFooBar'>)]
        
        
        `getFactories(ifaces)`
        ----------------------
        
        Return the factory registrations of the factories that will return objects
        providing this interface.
        
        Again, the first step is to create some factories:
        
          >>> from zope.component.factory import Factory
          >>> from zope.component.interfaces import IFactory
          >>> ztapi.provideUtility(IFactory, Factory(MyFoo), 'MyFoo')
          >>> ztapi.provideUtility(IFactory, Factory(MyBar), 'MyBar')
          >>> ztapi.provideUtility(IFactory,
          ...     Factory(MyFooBar, 'MyFooBar', 'My Foo Bar'), 'MyFooBar')
        
        Let's see whether we will be able to get them:
        
          >>> regs = list(component.getFactories(IFooBar))
          >>> regs.sort()
          >>> regs
          [UtilityRegistration(<BaseGlobalComponents base>,
              IFactory, 'MyFooBar',
              <Factory for <class 'zope.app.apidoc.doctest.MyFooBar'>>, None, u'')]
        
          >>> regs = list(component.getFactories(IFoo))
          >>> regs.sort()
          >>> regs
          [UtilityRegistration(<BaseGlobalComponents base>, IFactory, 'MyFoo',
                       <Factory for <class 'zope.app.apidoc.doctest.MyFoo'>>, None, u''),
           UtilityRegistration(<BaseGlobalComponents base>, IFactory, 'MyFooBar',
                    <Factory for <class 'zope.app.apidoc.doctest.MyFooBar'>>, None, u'')]
        
        
        `getUtilities(iface)`
        ---------------------
        
        Return all utility registrations for utilities that provide the specified
        interface.
        
        As usual, we have to register some utilities first:
        
          >>> ztapi.provideUtility(IFoo, MyFoo())
          >>> ztapi.provideUtility(IBar, MyBar())
          >>> ztapi.provideUtility(IFooBar, MyFooBar())
        
        Now let's have a look what we have:
        
          >>> regs = list(component.getUtilities(IFooBar))
          >>> regs.sort()
          >>> regs #doctest:+ELLIPSIS
          [UtilityRegistration(<BaseGlobalComponents base>, IFooBar, '',
                               <zope.app.apidoc.doctest.MyFooBar object at ...>, None, u'')]
        
          >>> regs = list(component.getUtilities(IFoo))
          >>> regs.sort()
          >>> regs #doctest:+ELLIPSIS
          [UtilityRegistration(<BaseGlobalComponents base>, IFoo, '',
                               <zope.app.apidoc.doctest.MyFoo object at ...>, None, u''),
           UtilityRegistration(<BaseGlobalComponents base>, IFooBar, '',
                               <zope.app.apidoc.doctest.MyFooBar object at ...>, None, u'')]
        
        
        `getRealFactory(factory)`
        -------------------------
        
        During registration, factories are commonly masked by wrapper functions. Also,
        factories are sometimes also `IFactory` instances, which are not referencable,
        so that we would like to return the class. If the wrapper objects/functions
        play nice, then they provide a `factory` attribute that points to the next
        wrapper or the original factory.
        
        The task of this function is to remove all the factory wrappers and make sure
        that the returned factory is referencable.
        
          >>> class Factory(object):
          ...     pass
        
          >>> def wrapper1(*args):
          ...     return Factory(*args)
          >>> wrapper1.factory = Factory
        
          >>> def wrapper2(*args):
          ...     return wrapper1(*args)
          >>> wrapper2.factory = wrapper1
        
        So whether we pass in `Factory`,
        
          >>> component.getRealFactory(Factory)
          <class 'zope.app.apidoc.doctest.Factory'>
        
        `wrapper1`,
        
          >>> component.getRealFactory(wrapper1)
          <class 'zope.app.apidoc.doctest.Factory'>
        
        or `wrapper2`,
        
          >>> component.getRealFactory(wrapper2)
          <class 'zope.app.apidoc.doctest.Factory'>
        
        the answer should always be the `Factory` class. Next we are going to pass in
        an instance, and again we should get our class aas a result:
        
          >>> factory = Factory()
          >>> component.getRealFactory(factory)
          <class 'zope.app.apidoc.doctest.Factory'>
        
        Even, if the factory instance is wrapped, we should get the factory class:
        
          >>> def wrapper3(*args):
          ...     return factory(*args)
          >>> wrapper3.factory = factory
        
          >>> component.getRealFactory(wrapper3)
          <class 'zope.app.apidoc.doctest.Factory'>
        
        
        `getInterfaceInfoDictionary(iface)`
        -----------------------------------
        
        This function returns a small info dictionary for an interface. It only
        reports the module and the name. This is useful for cases when we only want to
        list interfaces in the context of other components, like adapters and
        utilities.
        
          >>> from pprint import pprint
          >>> pprint(component.getInterfaceInfoDictionary(IFoo), width=1)
          {'module': 'zope.app.apidoc.doctest', 'name': 'IFoo'}
        
        The functions using this function use it with little care and can also
        sometimes pass in `None`. In these cases we want to return `None`:
        
          >>> component.getInterfaceInfoDictionary(None) is None
          True
        
        It's also possible for this function to be passed a
        zope.interface.declarations.Implements instance.  For instance, this function
        is sometimes used to analyze the required elements of an adapter registration:
        if an adapter or subscriber is registered against a class, then the required
        element will be an Implements instance.  In this case, we currently believe
        that we want to return the module and name of the object that the Implements
        object references.  This may change.
        
          >>> from zope.interface import implementedBy
          >>> pprint(component.getInterfaceInfoDictionary(implementedBy(MyFoo)), width=1)
          {'module': 'zope.app.apidoc.doctest', 'name': 'MyFoo'}
        
        
        `getTypeInfoDictionary(type)`
        -----------------------------
        
        This function returns the info dictionary of a type.
        
          >>> pprint(component.getTypeInfoDictionary(tuple), width=1)
          {'module': '__builtin__',
           'name': 'tuple',
           'url': '__builtin__/tuple'}
        
        
        `getSpecificationInfoDictionary(spec)`
        --------------------------------------
        
        Thsi function returns an info dictionary for the given specification. A
        specification can either be an interface or class. If it is an interface, it
        simply returns the interface dictionary:
        
          >>> pprint(component.getSpecificationInfoDictionary(IFoo))
          {'isInterface': True,
           'isType': False,
           'module': 'zope.app.apidoc.doctest',
           'name': 'IFoo'}
        
        In addition to the usual interface infos, there are two flags indicating
        whether the specification was an interface or type. In our case it is an
        interface.
        
        Let's now look at the behavior when passing a type:
        
          >>> import zope.interface
          >>> tupleSpec = zope.interface.implementedBy(tuple)
        
          >>> pprint(component.getSpecificationInfoDictionary(tupleSpec))
          {'isInterface': False,
           'isType': True,
           'module': '__builtin__',
           'name': 'tuple',
           'url': '__builtin__/tuple'}
        
        For the type, we simply reuse the type info dictionary function.
        
        
        `getAdapterInfoDictionary(reg)`
        -------------------------------
        
        This function returns a page-template-friendly dictionary representing the
        data of an adapter registration in an output-friendly format.
        
        Let's first create an adapter registration:
        
          >>> class MyResult(object):
          ...     implements(IResult)
        
          >>> from zope.component.registry import AdapterRegistration
          >>> reg = AdapterRegistration(None, (IFoo, IBar), IResult, 'FooToResult',
          ...                            MyResult, 'doc info')
        
        And now get the info dictionary:
        
          >>> pprint(component.getAdapterInfoDictionary(reg), width=1)
          {'doc': 'doc info',
           'factory': 'zope.app.apidoc.doctest.MyResult',
           'factory_url': 'zope/app/apidoc/doctest/MyResult',
           'name': u'FooToResult',
           'provided': {'module': 'zope.app.apidoc.doctest',
                        'name': 'IResult'},
           'required': [{'isInterface': True,
                         'isType': False,
                         'module': 'zope.app.apidoc.doctest',
                         'name': 'IFoo'},
                        {'isInterface': True,
                         'isType': False,
                         'module': 'zope.app.apidoc.doctest',
                         'name': 'IBar'}],
           'zcml': None}
        
        If the factory's path cannot be referenced, for example if a type has been
        created using the ``type()`` builtin function, then the URL of the factory
        will be ``None``:
        
          >>> MyResultType = type('MyResult2', (object,), {})
          >>> from zope.interface import classImplements
          >>> classImplements(MyResultType, IResult)
        
          >>> reg = AdapterRegistration(None, (IFoo, IBar), IResult, 'FooToResult',
          ...                            MyResultType, 'doc info')
          >>> pprint(component.getAdapterInfoDictionary(reg), width=1)
          {'doc': 'doc info',
           'factory': 'zope.app.apidoc.doctest.MyResult2',
           'factory_url': None,
           'name': u'FooToResult',
           'provided': {'module': 'zope.app.apidoc.doctest',
                        'name': 'IResult'},
           'required': [{'isInterface': True,
                         'isType': False,
                         'module': 'zope.app.apidoc.doctest',
                         'name': 'IFoo'},
                        {'isInterface': True,
                         'isType': False,
                         'module': 'zope.app.apidoc.doctest',
                         'name': 'IBar'}],
           'zcml': None}
        
        This function can also handle subscription registrations, which are pretty
        much like adapter registrations, except that they do not have a name. So let's
        see how the function handles subscriptions:
        
          >>> from zope.component.registry import HandlerRegistration
          >>> reg = HandlerRegistration(None, (IFoo, IBar), '', MyResult, 'doc info')
        
          >>> pprint(component.getAdapterInfoDictionary(reg))
          {'doc': 'doc info',
           'factory': 'zope.app.apidoc.doctest.MyResult',
           'factory_url': 'zope/app/apidoc/doctest/MyResult',
           'name': u'',
           'provided': None,
           'required': [{'isInterface': True,
                         'isType': False,
                         'module': 'zope.app.apidoc.doctest',
                         'name': 'IFoo'},
                        {'isInterface': True,
                         'isType': False,
                         'module': 'zope.app.apidoc.doctest',
                         'name': 'IBar'}],
           'zcml': None}
        
        
        `getFactoryInfoDictionary(reg)`
        -------------------------------
        
        This function returns a page-template-friendly dictionary representing the
        data of a factory (utility) registration in an output-friendly format.
        
        Luckily we have already registered some factories, so we just reuse their
        registrations:
        
          >>> pprint(component.getFactoryInfoDictionary(
          ...     component.getFactories(IFooBar).next()))
          {'description': u'<p>My Foo Bar</p>\n',
           'name': u'MyFooBar',
           'title': 'MyFooBar',
           'url': 'zope/app/apidoc/doctest/MyFooBar'}
        
        If the factory's path cannot be referenced, for example if a type has been
        created using the ``type()`` builtin function, then the URL of the factory
        will be ``None``:
        
          >>> class IMine(Interface):
          ...     pass
        
          >>> class FactoryBase(object):
          ...     def getInterfaces(self): return [IMine]
        
          >>> MyFactoryType = type('MyFactory', (FactoryBase,), {})
          >>> from zope.interface import classImplements
          >>> classImplements(MyFactoryType, IFactory)
          >>> ztapi.provideUtility(IFactory, MyFactoryType(), 'MyFactory')
        
          >>> pprint(component.getFactoryInfoDictionary(
          ...     component.getFactories(IMine).next()), width=1)
          {'description': u'',
           'name': u'MyFactory',
           'title': u'',
           'url': None}
        
        
        `getUtilityInfoDictionary(name, factory)`
        -----------------------------------------
        
        This function returns a page-template-friendly dictionary representing the
        data of a utility registration in an output-friendly format.
        
        Luckily we have already registered some utilities, so we just reuse their
        registrations:
        
          >>> pprint(component.getUtilityInfoDictionary(
          ...     component.getUtilities(IFooBar).next()))
          {'iface_id': 'zope.app.apidoc.doctest.IFooBar',
           'name': u'<i>no name</i>',
           'path': 'zope.app.apidoc.doctest.MyFooBar',
           'url': 'Code/zope/app/apidoc/doctest/MyFooBar',
           'url_name': 'X19ub25hbWVfXw=='}
        
        
        ==============================
        Interface Inspection Utilities
        ==============================
        
        This document is a presentation of the utility functions provided by
        
          >>> from zope.app.apidoc import interface
        
        For the following demonstrations, we need a nice interface that we can inspect:
        
          >>> from zope.interface import Interface, Attribute
          >>> from zope.schema import Field, TextLine
        
          >>> class IFoo(Interface):
          ...     foo = Field(title=u"Foo")
          ...
          ...     bar = TextLine(title=u"Bar",
          ...                    description=u"The Bar",
          ...                    required=True,
          ...                    default=u"My Bar")
          ...
          ...     baz = Attribute('baz',
          ...                     'This is the baz attribute')
          ...
          ...     def blah(one, two, three=None, *args, **kwargs):
          ...         """This is the `blah` method."""
        
        
        `getElements(iface, type=IElement)`
        -----------------------------------
        
        Return a dictionary containing all elements in an interface. The type
        specifies whether we are looking for attributes, fields or methods. So let's
        look at an example.
        
        First, let's get the methods of an interface:
        
          >>> from zope.interface.interfaces import IMethod
          >>> interface.getElements(IFoo, type=IMethod).keys()
          ['blah']
        
        and now the fields:
        
          >>> from zope.schema.interfaces import IField
          >>> names = interface.getElements(IFoo, type=IField).keys()
          >>> names.sort()
          >>> names
          ['bar', 'foo']
        
        We can also get all attributes of course.
        
          >>> from zope.interface.interfaces import IAttribute
          >>> names = interface.getElements(IFoo, type=IAttribute).keys()
          >>> names.sort()
          >>> names
          ['bar', 'baz', 'blah', 'foo']
        
        You might be surprised by the above result, since the fields and methods are
        again included. However, fields and methods are just attributes and thus
        extend the simple attribute implementation. If you want to get a list of
        attributes that does not include fields and methods, see the
        `getAttributes(iface)` function.
        
        The default type is `IElement` which will simply return all elements of the
        interface:
        
          >>> names = interface.getElements(IFoo).keys()
          >>> names.sort()
          >>> names
          ['bar', 'baz', 'blah', 'foo']
        
        Note: The interface you pass to this function *cannot* be proxied!
        Presentation code often like to wrap interfaces in security proxies and apidoc
        even uses location proxies for interface.
        
        
        `getFieldsInOrder(iface, _itemsorter=...)`
        -----------------------------------------------------------
        
        For presentation purposes we often want fields to have the a certain order,
        most comonly the order they have in the interface. This function returns a
        list of (name, field) tuples in a specified order.
        
        The `_itemsorter` argument provides the function that is used to order the
        fields. The default function, which sorts by the fields' `order` attribute,
        should be the correct one for 99% of your needs.
        
        Reusing the interface created above, we check the output:
        
          >>> [n for n, a in interface.getFieldsInOrder(IFoo)]
          ['foo', 'bar']
        
        By changing the sort method to sort by names, we get:
        
          >>> [n for n, a in interface.getFieldsInOrder(
          ...       IFoo, _itemsorter=lambda x, y: cmp(x[0], y[0]))]
          ['bar', 'foo']
        
        
        `getAttributes(iface)`
        ----------------------
        
        This function returns a (name, attr) tuple for every attribute in the
        interface. Note that this function will only return pure attributes; it
        ignores methods and fields.
        
          >>> attrs = interface.getAttributes(IFoo)
          >>> attrs.sort()
          >>> attrs #doctest: +ELLIPSIS
          [('baz', <zope.interface.interface.Attribute object at ...>)]
        
        
        `getMethods(iface)`
        -------------------
        
        This function returns a (name, method) tuple for every declared method in the
        interface.
        
          >>> methods = interface.getMethods(IFoo)
          >>> methods.sort()
          >>> methods #doctest: +ELLIPSIS
          [('blah', <zope.interface.interface.Method object at ...>)]
        
        
        `getFields(iface)`
        ------------------
        
        This function returns a (name, field) tuple for every declared field in the
        interface.
        
          >>> interface.getFields(IFoo) #doctest: +ELLIPSIS
          [('foo', <zope.schema._bootstrapfields.Field object at ...>),
           ('bar', <zope.schema._bootstrapfields.TextLine object at ...>)]
        
        Note that this returns the same result as `getFieldsInOrder()` with the fields
        sorted by their `order` attribute, except that you cannot specify the sort
        function here. This function was mainly provided for symmetry with the other
        functions.
        
        
        `getInterfaceTypes(iface)`
        --------------------------
        
        Interfaces can be categorized/grouped by using interface types. Interface
        types simply extend `zope.interface.interfaces.IInterface`, which are
        basically meta-interfaces. The interface types are then provided by particular
        interfaces.
        
        The `getInterfaceTypes()` function returns a list of interface types that are
        provided for the specified interface. Note that you commonly expect only one
        type per interface, though.
        
        Before we assign any type to our `IFoo` interface, there are no types
        declared.
        
          >>> interface.getInterfaceTypes(IFoo)
          []
        
        Now we define a new type called `IContentType`
        
          >>> from zope.interface.interfaces import IInterface
          >>> class IContentType(IInterface):
          ...     pass
        
        and have our interface provide it:
        
          >>> from zope.interface import directlyProvides
          >>> directlyProvides(IFoo, IContentType)
        
        Note that ZCML has some more convenient methods of doing this. Now let's get
        the interface types again:
        
          >>> interface.getInterfaceTypes(IFoo)
          [<InterfaceClass zope.app.apidoc.doctest.IContentType>]
        
        Again note that the interface passed to this function *cannot* be proxied,
        otherwise this method will pick up the proxy's interfaces as well.
        
        
        `getFieldInterface(field)`
        --------------------------
        
        This function tries pretty hard to determine the best-matching interface that
        represents the field. Commonly the field class has the same name as the field
        interface (minus an "I"). So this is our first choice:
        
          >>> from zope.schema import Text, Int
          >>> interface.getFieldInterface(Text())
          <InterfaceClass zope.schema.interfaces.IText>
        
          >>> interface.getFieldInterface(Int())
          <InterfaceClass zope.schema.interfaces.IInt>
        
        If the name matching method fails, it picks the first interface that extends
        `IField`:
        
          >>> from zope.schema.interfaces import IField
          >>> class ISpecialField(IField):
          ...     pass
          >>> class ISomething(Interface):
          ...     pass
        
          >>> from zope.interface import implements
          >>> class MyField:
          ...     implements(ISomething, ISpecialField)
        
          >>> interface.getFieldInterface(MyField())
          <InterfaceClass zope.app.apidoc.doctest.ISpecialField>
        
        
        `getAttributeInfoDictionary(attr, format='restructuredtext')`
        -------------------------------------------------------------
        
        This function returns a page-template-friendly dictionary for a simple
        attribute:
        
          >>> from pprint import pprint
          >>> pprint(interface.getAttributeInfoDictionary(IFoo['baz']))
          {'doc': u'<p>This is the baz attribute</p>\n',
           'name': 'baz'}
        
        
        `getMethodInfoDictionary(method, format='restructuredtext')`
        -------------------------------------------------------------
        
        This function returns a page-template-friendly dictionary for a method:
        
          >>> pprint(interface.getMethodInfoDictionary(IFoo['blah'])) #doc
          {'doc':
             u'<p>This is the <cite>blah</cite> method.</p>\n',
           'name': 'blah',
           'signature': '(one, two, three=None, *args, **kwargs)'}
        
        
        `getFieldInfoDictionary(field, format='restructuredtext')`
        ----------------------------------------------------------
        
        This function returns a page-template-friendly dictionary for a field:
        
          >>> pprint(interface.getFieldInfoDictionary(IFoo['bar']), width=1)
          {'class': {'name': 'TextLine',
                     'path': 'zope/schema/_bootstrapfields/TextLine'},
           'default': "u'My Bar'",
           'description': u'<p>The Bar</p>\n',
           'iface': {'id': 'zope.schema.interfaces.ITextLine',
                     'name': 'ITextLine'},
           'name': 'bar',
           'required': True,
           'required_string': u'required',
           'title': u'Bar'}
        
        
        =================================
        Presentation Inspection Utilities
        =================================
        
        The `presentation` module provides some nice utilities to inspect presentation
        registrations.
        
          >>> from zope.app.apidoc import presentation
        
        
        `getViewFactoryData(factory)`
        -----------------------------
        
        This function tries really hard to determine the correct information about a
        view factory. For example, when you create a page, a new type is dynamically
        generated upon registration. Let's look at a couple examples.
        
        First, let's inspect a case where a simple browser page was configured without
        a special view class. In these cases the factory is a `SimpleViewClass`:
        
          >>> from zope.browserpage.simpleviewclass import SimpleViewClass
          >>> view = SimpleViewClass('browser/index.pt')
          >>> info = presentation.getViewFactoryData(view)
        
        Before we can check the result, we have to make sure that all Windows paths
        are converted to Unix-like paths. We also clip off instance-specific parts of
        the template path:
        
          >>> info['template'] = info['template'].replace('\\', '/')[-32:]
          >>> from pprint import pprint
          >>> pprint(info)
          {'path': 'zope.browserpage.simpleviewclass.simple',
           'referencable': True,
           'resource': None,
           'template': 'zope/app/apidoc/browser/index.pt',
           'template_obj': <BoundPageTemplateFile of None>,
           'url': 'zope/browserpage/simpleviewclass/simple'}
        
        So in the result above we see what the function returns. It is a dictionary
        (converted to a list for test purposes) that contains the Python path of the
        view class, a flag that specifies whether the factory can be referenced and
        thus be viewed by the class browser, the (page) template used for the view and
        the URL under which the factory will be found in the class browser. Some
        views, like icons, also use resources to provide their data. In these cases
        the name of the resource will be provided. Of course, not in all cases all
        values will be available. Empty values are marked with `None`.
        
        Believe it or not, in some cases the factory is just a simple type. In these
        cases we cannot retrieve any useful information:
        
          >>> info = presentation.getViewFactoryData(3)
          >>> pprint(info)
          {'path': None,
           'referencable': False,
           'resource': None,
           'template': None,
           'url': None}
        
        In some cases factories are callable class instances, where we cannot directly
        have a referencable name, so we lookup the class and use its name:
        
          >>> class Factory(object):
          ...     pass
        
          >>> info = presentation.getViewFactoryData(Factory())
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        One of the more common cases, however, is that the factory is a class or
        type. In this case we can just retrieve the reference directly:
        
          >>> info = presentation.getViewFactoryData(Factory)
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        When factories are created by a directive, they can also be functions. In
        those cases we just simply return the function path:
        
          >>> def factory():
          ...     pass
        
          # The testing framework does not set the __module__ correctly
          >>> factory.__module__ = '__builtin__'
        
          >>> info = presentation.getViewFactoryData(factory)
          >>> pprint(info)
          {'path': '__builtin__.factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/factory'}
        
        However, the function is rather unhelpful, since it will be the same for all
        views that use that code path. For this reason the function keeps track of the
        original factory component in a function attribute called ``factory``:
        
          >>> factory.factory = Factory
        
          >>> info = presentation.getViewFactoryData(factory)
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        Let's now have a look at some extremly specific cases. If a view is registered
        using the ``zope:view`` directive and a permission is specified, a
        ``ProxyView`` class instance is created that references its original factory:
        
          >>> class ProxyView(object):
          ...
          ...     def __init__(self, factory):
          ...         self.factory = factory
          >>> proxyView = ProxyView(Factory)
        
          >>> info = presentation.getViewFactoryData(proxyView)
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        Another use case is when a new type is created by the ``browser:page`` or
        ``browser:view`` directive. In those cases the true/original factory is really
        the first base class. Those cases are detected by inspecting the
        ``__module__`` string of the type:
        
          >>> new_class = type(Factory.__name__, (Factory,), {})
          >>> new_class.__module__ = 'zope.app.publisher.browser.viewmeta'
        
          >>> info = presentation.getViewFactoryData(new_class)
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        The same sort of thing happens for XML-RPC views, except that those are
        wrapped twice:
        
          >>> new_class = type(Factory.__name__, (Factory,), {})
          >>> new_class.__module__ = 'zope.app.publisher.xmlrpc.metaconfigure'
        
          >>> new_class2 = type(Factory.__name__, (new_class,), {})
          >>> new_class2.__module__ = 'zope.app.publisher.xmlrpc.metaconfigure'
        
          >>> info = presentation.getViewFactoryData(new_class2)
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        Finally, it sometimes happens that a factory is wrapped and the wrapper is
        wrapped in return:
        
          >>> def wrapper1(*args):
          ...     return Factory(*args)
        
          >>> def wrapper2(*args):
          ...     return wrapper1(*args)
        
        Initially, the documentation is not very helpful:
        
          >>> info = presentation.getViewFactoryData(wrapper2)
          >>> pprint(info)
          {'path': 'None.wrapper2',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': 'None/wrapper2'}
        
        However, if those wrappers play nicely, they provide a factory attribute each
        step of the way ...
        
          >>> wrapper1.factory = Factory
          >>> wrapper2.factory = wrapper1
        
        and the result is finally our original factory:
        
          >>> info = presentation.getViewFactoryData(wrapper2)
          >>> pprint(info)
          {'path': '__builtin__.Factory',
           'referencable': True,
           'resource': None,
           'template': None,
           'url': '__builtin__/Factory'}
        
        
        `getPresentationType(iface)`
        ----------------------------
        
        In Zope 3, presentation types (i.e. browser, ftp, ...) are defined through
        their special request interface, such as `IBrowserRequest` or
        `IFTPRequest`. To complicate matters further, layer interfaces are used in
        browser presentations to allow skinning. Layers extend any request type, but
        most commonly `IBrowserRequest`. This function inspects the request interface
        of any presentation multi-adapter and determines its type, which is returned
        in form of an interface.
        
          >>> from zope.app.apidoc.presentation import getPresentationType
          >>> from zope.publisher.interfaces.http import IHTTPRequest
          >>> from zope.publisher.interfaces.browser import IBrowserRequest
        
          >>> class ILayer1(IBrowserRequest):
          ...     pass
        
          >>> presentation.getPresentationType(ILayer1)
          <InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>
        
          >>> class ILayer2(IHTTPRequest):
          ...     pass
        
          >>> presentation.getPresentationType(ILayer2)
          <InterfaceClass zope.publisher.interfaces.http.IHTTPRequest>
        
        If the function cannot determine the presentation type, the interface itself
        is returned:
        
          >>> from zope.interface import Interface
          >>> class ILayer3(Interface):
          ...     pass
        
          >>> presentation.getPresentationType(ILayer3)
          <InterfaceClass __builtin__.ILayer3>
        
        Note that more specific presentation types are considered first. For example,
        `IBrowserRequest` extends `IHTTPRequest`, but it will always determine the
        presentation type to be an `IBrowserRequest`.
        
        
        `getViews(iface, type=IRequest)`
        --------------------------------
        
        This function retrieves all available view registrations for a given interface
        and presentation type. The default argument for the presentation type is
        `IRequest`, which will effectively return all views for the specified
        interface.
        
        To see how this works, we first have to register some views:
        
          >>> class IFoo(Interface):
          ...     pass
        
          >>> from zope.app.testing import ztapi
          >>> ztapi.provideAdapter((IFoo, IHTTPRequest), Interface, None, name='foo')
          >>> ztapi.provideAdapter((Interface, IHTTPRequest), Interface, None,
          ...                      name='bar')
          >>> ztapi.provideAdapter((IFoo, IBrowserRequest), Interface, None,
          ...                      name='blah')
        
        Now let's see what we've got. If we do not specify a type, all registrations
        should be returned:
        
          >>> regs = list(presentation.getViews(IFoo))
          >>> regs.sort()
          >>> regs #doctest:+ELLIPSIS
          [AdapterRegistration(<BaseGlobalComponents base>, 
                               [IFoo, IBrowserRequest], Interface, 'blah', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [IFoo, IHTTPRequest], Interface, 'foo', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                               [Interface, IHTTPRequest], Interface, 'bar', None, u'')]
        
          >>> regs = list(presentation.getViews(Interface, IHTTPRequest))
          >>> regs.sort()
          >>> regs #doctest:+ELLIPSIS
          [AdapterRegistration(<BaseGlobalComponents base>, 
                               [Interface, IHTTPRequest], Interface, 'bar', None, u'')]
        
        
        `filterViewRegistrations(regs, iface, level=SPECIFC_INTERFACE_LEVEL)`
        ---------------------------------------------------------------------
        
        Oftentimes the amount of views that are being returned for a particular
        interface are too much to show at once. It is then good to split the view into
        categories. The `filterViewRegistrations()` function allows you to filter the
        views on how specific they are to the interface. Here are the three levels you
        can select from:
        
          * SPECIFC_INTERFACE_LEVEL -- Only return registrations that require the
                                       specified interface directly.
        
          * EXTENDED_INTERFACE_LEVEL -- Only return registrations that require an
                                        interface that the specified interface extends.
        
          * GENERIC_INTERFACE_LEVEL -- Only return registrations that explicitely
                                       require the `Interface` interface.
        
        So, let's see how this is done. We first need to create a couple of interfaces
        and register some views:
        
          >>> class IContent(Interface):
          ...     pass
          >>> class IFile(IContent):
          ...     pass
        
          Clear out the registries first, so we know what we have.
          >>> from zope.testing.cleanup import cleanUp
          >>> cleanUp()
        
          >>> ztapi.provideAdapter((IContent, IHTTPRequest), Interface,
          ...                      None, name='view.html')
          >>> ztapi.provideAdapter((IContent, IHTTPRequest), Interface,
          ...                      None, name='edit.html')
          >>> ztapi.provideAdapter((IFile, IHTTPRequest), Interface,
          ...                      None, name='view.html')
          >>> ztapi.provideAdapter((Interface, IHTTPRequest), Interface,
          ...                      None, name='view.html')
        
        Now we get all the registrations:
        
          >>> regs = list(presentation.getViews(IFile, IHTTPRequest))
        
        Let's now filter those registrations:
        
          >>> result = list(presentation.filterViewRegistrations(
          ...     regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL))
          >>> result.sort()
          >>> result
          [AdapterRegistration(<BaseGlobalComponents base>, 
                             [IFile, IHTTPRequest], Interface, 'view.html', None, u'')]
        
          >>> result = list(presentation.filterViewRegistrations(
          ...     regs, IFile, level=presentation.EXTENDED_INTERFACE_LEVEL))
          >>> result.sort()
          >>> result
          [AdapterRegistration(<BaseGlobalComponents base>,
                          [IContent, IHTTPRequest], Interface, 'edit.html', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                          [IContent, IHTTPRequest], Interface, 'view.html', None, u'')]
        
          >>> result = list(presentation.filterViewRegistrations(
          ...     regs, IFile, level=presentation.GENERIC_INTERFACE_LEVEL))
          >>> result.sort()
          >>> result
          [AdapterRegistration(<BaseGlobalComponents base>,
                         [Interface, IHTTPRequest], Interface, 'view.html', None, u'')]
        
        You can also specify multiple levels at once using the Boolean OR operator,
        since all three levels are mutually exclusive.
        
          >>> result = list(presentation.filterViewRegistrations(
          ...     regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL |
          ...                        presentation.EXTENDED_INTERFACE_LEVEL))
          >>> result.sort()
          >>> result
          [AdapterRegistration(<BaseGlobalComponents base>, 
                          [IContent, IHTTPRequest], Interface, 'edit.html', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                          [IContent, IHTTPRequest], Interface, 'view.html', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                          [IFile, IHTTPRequest], Interface, 'view.html', None, u'')]
        
          >>> result = list(presentation.filterViewRegistrations(
          ...     regs, IFile, level=presentation.SPECIFIC_INTERFACE_LEVEL |
          ...                        presentation.GENERIC_INTERFACE_LEVEL))
          >>> result.sort()
          >>> result
          [AdapterRegistration(<BaseGlobalComponents base>,
                        [IFile, IHTTPRequest], Interface, 'view.html', None, u''),
           AdapterRegistration(<BaseGlobalComponents base>,
                        [Interface, IHTTPRequest], Interface, 'view.html', None, u'')]
        
        
        `getViewInfoDictionary(reg)`
        ----------------------------
        
        Now that we have all these utilities to select the registrations, we need to
        prepare the them for output. For page templates the best data structures are
        dictionaries and tuples/lists. This utility will generate an informational
        dictionary for the specified registration.
        
        Let's first create a registration:
        
          >>> from zope.component.registry import AdapterRegistration
          >>> reg = AdapterRegistration(None, (IFile, Interface, IHTTPRequest),
          ...                           Interface, 'view.html', Factory, 'reg info')
        
          >>> pprint(presentation.getViewInfoDictionary(reg), width=1)
          {'doc': 'reg info',
           'factory': {'path': '__builtin__.Factory',
                       'referencable': True,
                       'resource': None,
                       'template': None,
                       'url': '__builtin__/Factory'},
           'name': u'view.html',
           'provided': {'module': 'zope.interface',
                        'name': 'Interface'},
           'read_perm': None,
           'required': [{'module': '__builtin__',
                         'name': 'IFile'},
                        {'module': 'zope.interface',
                         'name': 'Interface'},
                        {'module': 'zope.publisher.interfaces.http',
                         'name': 'IHTTPRequest'}],
           'type': 'zope.publisher.interfaces.http.IHTTPRequest',
           'write_perm': None,
           'zcml': None}
        
        
        =======================
        Miscellaneous Utilities
        =======================
        
        The utilities module provides some useful helper functions and classes that
        make the work of the API doctool and inspection code easier.
        
          >>> from zope.app.apidoc import utilities
        
        
        `relativizePath(path)`
        ----------------------
        
        When dealing with files, such as page templates and text files, and not with
        Python paths, it is necessary to keep track of the the absolute path of the
        file. However, for presentation purposes, the absolute path is inappropriate
        and we are commonly interested in the path starting at the Zope 3 root
        directory. This function attempts to remove the absolute path to the root
        directory and replaces it with "Zope3".
        
          >>> import os
          >>> path = os.path.join(utilities.BASEDIR, 'src', 'zope', 'README.txt')
        
          >>> utilities.BASEDIR in path
          True
        
          >>> path = utilities.relativizePath(path)
        
          >>> utilities.BASEDIR in path
          False
        
          # Be kind to Windows users
          >>> path.replace('\\', '/')
          'Zope3/src/zope/README.txt'
        
        If the base path is not found in a particular path, the original path is
        returned:
        
          >>> otherpath = 'foo/bar/blah.txt'
          >>> utilities.relativizePath(otherpath)
          'foo/bar/blah.txt'
        
        
        `truncateSysPath(path)`
        -----------------------
        
        In some cases it is useful to just know the path after the sys path of a
        module. For example, you have a path of a file in a module. To look up the
        module, the simplest to do is to retrieve the module path and look into the
        system's modules list.
        
          >>> import sys
          >>> sysBase = sys.path[0]
        
          >>> utilities.truncateSysPath(sysBase + '/some/module/path')
          'some/module/path'
        
        If there is no matching system path, then the whole path is returned:
        
          >>> utilities.truncateSysPath('some/other/path')
          'some/other/path'
        
        
        `ReadContainerBase` (Class)
        ---------------------------
        
        This class serves as a base class for `IReadContainer` objects that minimizes
        the implementation of an `IReadContainer` to two methods, `get()` and
        `items()`, since the other methods can be implemented using these two.
        
        Note that this implementation might be very expensive for certain container,
        especially if collecting the items is of high order. However, there are many
        scenarios when one has a complete mapping already and simply want to persent
        it as an `IReadContainer`.
        
        Let's start by making a simple `IReadContainer` implementation using the
        class:
        
          >>> class Container(utilities.ReadContainerBase):
          ...     def get(self, key, default=None):
          ...         return {'a': 1, 'b': 2}.get(key, default)
          ...     def items(self):
          ...         return [('a', 1), ('b', 2)]
        
          >>> container = Container()
        
        Now we can use the methods. First `get()`
        
          >>> container.get('a')
          1
          >>> container.get('c') is None
          True
          >>> container['b']
          2
        
        and then `items()`
        
          >>> container.items()
          [('a', 1), ('b', 2)]
          >>> container.keys()
          ['a', 'b']
          >>> container.values()
          [1, 2]
        
        Then naturally, all the other methods work as well:
        
          * `__getitem__(key)`
        
            >>> container['a']
            1
            >>> container['c']
            Traceback (most recent call last):
            ...
            KeyError: 'c'
        
          * `__contains__(key)`
        
            >>> 'a' in container
            True
            >>> 'c' in container
            False
        
          * `keys()`
        
            >>> container.keys()
            ['a', 'b']
        
          * `__iter__()`
        
            >>> iterator = iter(container)
            >>> iterator.next()
            1
            >>> iterator.next()
            2
            >>> iterator.next()
            Traceback (most recent call last):
            ...
            StopIteration
        
          * `values()`
        
            >>> container.values()
            [1, 2]
        
          * `__len__()`
        
            >>> len(container)
            2
        
        
        `getPythonPath(obj)`
        --------------------
        
        Return the path of the object in standard Python dot-notation.
        
        This function makes only sense for objects that provide a name, since we
        cannot determine the path otherwise. Instances, for example, do not have a
        `__name__` attribute, so we would expect them to fail.
        
        For interfaces we simply get
        
          >>> from zope.interface import Interface
          >>> class ISample(Interface):
          ...     pass
        
          >>> utilities.getPythonPath(ISample)
          'zope.app.apidoc.doctest.ISample'
        
        and for classes
        
          >>> class Sample(object):
          ...     def sample(self):
          ...         pass
        
          >>> utilities.getPythonPath(Sample.sample)
          'zope.app.apidoc.doctest.Sample'
        
        One can also pass functions
        
          >>> def sample():
          ...     pass
        
          >>> utilities.getPythonPath(sample)
          'zope.app.apidoc.doctest.sample'
        
        and even methods. If a method is passed in, its class path is returned.
        
          >>> utilities.getPythonPath(Sample.sample)
          'zope.app.apidoc.doctest.Sample'
        
        Modules are another kind of objects that can return a python path:
        
          >>> utilities.getPythonPath(utilities)
          'zope.app.apidoc.utilities'
        
        Passing in `None` returns `None`:
        
          >>> utilities.getPythonPath(None)
        
        Clearly, instance lookups should fail:
        
          >>> utilities.getPythonPath(Sample())
          Traceback (most recent call last):
          ...
          AttributeError: 'Sample' object has no attribute '__name__'
        
        
        `isReferencable(path)`
        ----------------------
        
        Determine whether a path can be referenced in the API doc, usually by the code
        browser module. Initially you might think that all objects that have paths can
        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:
        
          >>> utilities.isReferencable(None)
          False
        
        By design we also do not document any private classes and functions:
        
          >>> utilities.isReferencable('some.path.to._Private')
          False
          >>> utilities.isReferencable('some.path.to.__Protected')
          False
          >>> utilities.isReferencable('zope.app.apidoc.__doc__')
          True
        
        Some objects might fake their module name, so that it does not exist:
        
          >>> utilities.isReferencable('foo.bar')
          False
        
        On the other hand, you might have a valid module, but non-existent attribute:
        
          >>> utilities.isReferencable('zope.app.apidoc.MyClass')
          False
        
        Note that this case is also used for types that are generated using the
        ``type()`` function:
        
          >>> mytype = type('MyType', (object,), {})
          >>> path = utilities.getPythonPath(mytype)
          >>> path
          'zope.app.apidoc.doctest.MyType'
        
          >>> utilities.isReferencable(path)
          False
        
        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):
          ...     pass
        
          >>> utilities.isReferencable('zope.app.apidoc.doctest.Singelton')
          True
        
          >>> Singelton = Singelton()
        
          >>> 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)`
        --------------------------------------------------------
        
        Get the permissions of a class attribute. The attribute is specified by name.
        
        Either the `klass` or the `checker` argument must be specified. If the class
        is specified, then the checker for it is looked up. Furthermore, this function
        only works with `INameBasedChecker` checkers. If another checker is found,
        ``None`` is returned for the permissions.
        
        We start out by defining the class and then the checker for it:
        
          >>> from zope.security.checker import Checker, defineChecker
          >>> from zope.security.checker import CheckerPublic
        
          >>> class Sample(object):
          ...     attr = 'value'
          ...     attr3 = 'value3'
        
          >>> class Sample2(object):
          ...      pass
        
          >>> checker = Checker({'attr': 'zope.Read', 'attr3': CheckerPublic},
          ...                   {'attr': 'zope.Write', 'attr3': CheckerPublic})
          >>> defineChecker(Sample, checker)
        
        Now let's see how this function works:
        
          >>> entries = utilities.getPermissionIds('attr', klass=Sample)
          >>> entries['read_perm']
          'zope.Read'
          >>> entries['write_perm']
          'zope.Write'
        
          >>> from zope.security.checker import getCheckerForInstancesOf
          >>> entries = utilities.getPermissionIds('attr',
          ...                                      getCheckerForInstancesOf(Sample))
          >>> entries['read_perm']
          'zope.Read'
          >>> entries['write_perm']
          'zope.Write'
        
        The `Sample` class does not know about the `attr2` attribute:
        
          >>> entries = utilities.getPermissionIds('attr2', klass=Sample)
          >>> print entries['read_perm']
          n/a
          >>> print entries['write_perm']
          n/a
        
        The `Sample2` class does not have a checker:
        
          >>> entries = utilities.getPermissionIds('attr', klass=Sample2)
          >>> entries['read_perm'] is None
          True
          >>> print entries['write_perm'] is None
          True
        
        Finally, the `Sample` class' `attr3` attribute is public:
        
          >>> entries = utilities.getPermissionIds('attr3', klass=Sample)
          >>> print entries['read_perm']
          zope.Public
          >>> print entries['write_perm']
          zope.Public
        
        
        `getFunctionSignature(func)`
        ----------------------------
        
        Return the signature of a function or method. The `func` argument *must* be a
        generic function or a method of a class.
        
        First, we get the signature of a function that has a specific positional and
        keyword argument:
        
          >>> def func(attr, attr2=None):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '(attr, attr2=None)'
        
        Here is a function that has an unspecified amount of keyword arguments:
        
          >>> def func(attr, **kw):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '(attr, **kw)'
        
        And here we mix specified and unspecified keyword arguments:
        
          >>> def func(attr, attr2=None, **kw):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '(attr, attr2=None, **kw)'
        
        In the next example we have unspecified positional and keyword arguments:
        
          >>> def func(*args, **kw):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '(*args, **kw)'
        
        And finally an example, where we have on unspecified keyword arguments without
        any positional arguments:
        
          >>> def func(**kw):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '(**kw)'
        
        Next we test whether the signature is correctly determined for class
        methods. Note that the `self` argument is removed from the signature, since it
        is not essential for documentation.
        
        We start out with a simple positional argument:
        
          >>> class Klass(object):
          ...     def func(self, attr):
          ...         pass
          >>> utilities.getFunctionSignature(Klass.func)
          '(attr)'
        
        Next we have specific and unspecified positional arguments as well as
        unspecified keyword arguments:
        
          >>> class Klass(object):
          ...     def func(self, attr, *args, **kw):
          ...         pass
          >>> utilities.getFunctionSignature(Klass.func)
          '(attr, *args, **kw)'
        
        If you do not pass a function or method to the function, it will fail:
        
          >>> utilities.getFunctionSignature('func')
          Traceback (most recent call last):
          ...
          TypeError: func must be a function or method
        
        A very uncommon, but perfectly valid, case is that tuple arguments are
        unpacked inside the argument list of the function. Here is an example:
        
          >>> def func((arg1, arg2)):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '((arg1, arg2))'
        
        Even default assignment is allowed:
        
          >>> def func((arg1, arg2)=(1, 2)):
          ...     pass
          >>> utilities.getFunctionSignature(func)
          '((arg1, arg2)=(1, 2))'
        
        However, lists of this type are not allowed inside the argument list:
        
          >>> def func([arg1, arg2]):
          ...     pass
          Traceback (most recent call last):
          ...
          SyntaxError: invalid syntax
        
        Internal assignment is also not legal:
        
          >>> def func((arg1, arg2=1)):
          ...     pass
          Traceback (most recent call last):
          ...
          SyntaxError: invalid syntax
        
        
        `getPublicAttributes(obj)`
        --------------------------
        
        Return a list of public attribute names for a given object.
        
        This excludes any attribute starting with '_', which includes attributes of
        the form `__attr__`, which are commonly considered public, but they are so
        special that they are excluded. The `obj` argument can be either a classic
        class, type or instance of the previous two. Note that the term "attributes"
        here includes methods and properties.
        
        First we need to create a class with some attributes, properties and methods:
        
          >>> class Nonattr(object):
          ...     def __get__(*a):
          ...         raise AttributeError('nonattr')
        
          >>> class Sample(object):
          ...     attr = None
          ...     def __str__(self):
          ...         return ''
          ...     def func(self):
          ...         pass
          ...     def _getAttr(self):
          ...         return self.attr
          ...     attr2 = property(_getAttr)
          ...
          ...     nonattr = Nonattr() # Should not show up in public attrs
        
        We can simply pass in the class and get the public attributes:
        
          >>> attrs = utilities.getPublicAttributes(Sample)
          >>> attrs.sort()
          >>> attrs
          ['attr', 'attr2', 'func']
        
        Note that we exclude attributes that would raise attribute errors,
        like our silly Nonattr.
        
        But an instance of that class will work as well.
        
          >>> attrs = utilities.getPublicAttributes(Sample())
          >>> attrs.sort()
          >>> attrs
          ['attr', 'attr2', 'func']
        
        The function will also take inheritance into account and return all inherited
        attributes as well:
        
          >>> class Sample2(Sample):
          ...     attr3 = None
        
          >>> attrs = utilities.getPublicAttributes(Sample2)
          >>> attrs.sort()
          >>> attrs
          ['attr', 'attr2', 'attr3', 'func']
        
        
        `getInterfaceForAttribute(name, interfaces=_marker, klass=_marker, asPath=True)`
        --------------------------------------------------------------------------------
        
        Determine the interface in which an attribute is defined. This function is
        nice, if you have an attribute name which you retrieved from a class and want
        to know which interface requires it to be there.
        
        Either the `interfaces` or `klass` argument must be specified. If `interfaces`
        is not specified, the `klass` is used to retrieve a list of
        interfaces. `interfaces` must be iterable.
        
        `asPath` specifies whether the dotted name of the interface or the interface
        object is returned.
        
        First, we need to create some interfaces and a class that implements them:
        
          >>> from zope.interface import Interface, Attribute, implements
          >>> class I1(Interface):
          ...     attr = Attribute('attr')
        
          >>> class I2(I1):
          ...     def getAttr():
          ...         '''get attr'''
        
          >>> class Sample(object):
          ...     implements(I2)
        
        First we check whether an aatribute can be found in a list of interfaces:
        
          >>> utilities.getInterfaceForAttribute('attr', (I1, I2), asPath=False)
          <InterfaceClass zope.app.apidoc.doctest.I1>
          >>> utilities.getInterfaceForAttribute('getAttr', (I1, I2), asPath=False)
          <InterfaceClass zope.app.apidoc.doctest.I2>
        
        Now we are repeating the same lookup, but using the class, instead of a list
        of interfaces:
        
          >>> utilities.getInterfaceForAttribute('attr', klass=Sample, asPath=False)
          <InterfaceClass zope.app.apidoc.doctest.I1>
          >>> utilities.getInterfaceForAttribute('getAttr', klass=Sample, asPath=False)
          <InterfaceClass zope.app.apidoc.doctest.I2>
        
        By default, `asPath` is `True`, which means the path of the interface is
        returned:
        
          >>> utilities.getInterfaceForAttribute('attr', (I1, I2))
          'zope.app.apidoc.doctest.I1'
        
        If no match is found, ``None`` is returned.
        
          >>> utilities.getInterfaceForAttribute('attr2', (I1, I2)) is None
          True
          >>> utilities.getInterfaceForAttribute('attr2', klass=Sample) is None
          True
        
        If both, the `interfaces` and `klass` argument are missing, raise an error:
        
          >>> utilities.getInterfaceForAttribute('getAttr')
          Traceback (most recent call last):
          ...
          ValueError: need to specify interfaces or klass
        
        Similarly, it does not make sense if both are specified:
        
          >>> utilities.getInterfaceForAttribute('getAttr', interfaces=(I1,I2),
          ...                                    klass=Sample)
          Traceback (most recent call last):
          ...
          ValueError: must specify only one of interfaces and klass
        
        
        `columnize(entries, columns=3)`
        -------------------------------
        
        This function places a list of entries into columns.
        
        Here are some examples:
        
          >>> utilities.columnize([1], 3)
          [[1]]
        
          >>> utilities.columnize([1, 2], 3)
          [[1], [2]]
        
          >>> utilities.columnize([1, 2, 3], 3)
          [[1], [2], [3]]
        
          >>> utilities.columnize([1, 2, 3, 4], 3)
          [[1, 2], [3], [4]]
        
          >>> utilities.columnize([1], 2)
          [[1]]
        
          >>> utilities.columnize([1, 2], 2)
          [[1], [2]]
        
          >>> utilities.columnize([1, 2, 3], 2)
          [[1, 2], [3]]
        
          >>> utilities.columnize([1, 2, 3, 4], 2)
          [[1, 2], [3, 4]]
        
        
        `getDocFormat(module)`
        ----------------------
        
        This function inspects a module to determine the supported documentation
        format. The function returns a valid renderer source factory id.
        
        If the `__docformat__` module attribute is specified, its value will be used
        to look up the factory id:
        
          >>> from zope.app.apidoc import apidoc
          >>> utilities.getDocFormat(apidoc)
          'zope.source.rest'
        
        By default structured text is returned:
        
          >>> from zope.app.apidoc import tests
          >>> utilities.getDocFormat(tests)
          'zope.source.stx'
        
        This is a sensible default, since we only decided later in development to
        endorse restructured text, so that many files are still in the structured text
        format. All converted and new modules will have the `__docformat__` attribute.
        
        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'
        
        
        `dedentString(text)`
        ---------------------
        
        Before doc strings can be processed using STX or ReST they must be dendented,
        since otherwise the output will be incorrect. Let's have a look at some
        docstrings and see how they are correctly dedented.
        
        Let's start with a simple one liner. Nothing should happen:
        
          >>> def func():
          ...     '''One line documentation string'''
        
          >>> utilities.dedentString(func.__doc__)
          'One line documentation string'
        
        Now what about one line docstrings that start on the second line? While this
        format is discouraged, it is frequently used:
        
          >>> def func():
          ...     '''
          ...     One line documentation string
          ...     '''
        
          >>> utilities.dedentString(func.__doc__)
          '\nOne line documentation string\n'
        
        We can see that the leading whitespace on the string is removed, but not the
        newline character. Let's now try a simple multi-line docstring:
        
          >>> def func():
          ...     '''Short description
          ...
          ...     Lengthy description, giving some more background information and
          ...     discuss some edge cases.
          ...     '''
        
          >>> print utilities.dedentString(func.__doc__)
          Short description
          <BLANKLINE>
          Lengthy description, giving some more background information and
          discuss some edge cases.
          <BLANKLINE>
        
        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? The smallest amount of indentation is
        chosen:
        
          >>> def func():
          ...     '''Short description
          ...
          ...     Root Level
          ...
          ...       Second Level
          ...     '''
        
          >>> print utilities.dedentString(func.__doc__)
          Short description
          <BLANKLINE>
          Root Level
          <BLANKLINE>
            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)`
        --------------------------------------------
        
        A function that quickly renders the given text using the specified format.
        
        If the `module` argument is specified, the function will try to determine the
        format using the module. If the `format` argument is given, it is simply
        used. Clearly, you cannot specify both, the `module` and `format` argument.
        
        You specify the format as follows:
        
          >>> utilities.renderText('Hello!\n', format='zope.source.rest')
          u'<p>Hello!</p>\n'
        
        Note that the format string must be a valid source factory id; if the factory
        id is not given, 'zope.source.stx' is used. Thus, specifying the module is
        often safer (if available):
        
          >>> utilities.renderText('Hello!\n', module=apidoc)
          u'<p>Hello!</p>\n'
        
        
        ==================
        The Class Registry
        ==================
        
        This little registry allows us to quickly query a complete list of classes
        that are defined and used by Zope 3. The prime feature of the class is the
        ``getClassesThatImplement(iface)`` method that returns all classes that
        implement the passed interface. Another method, ``getSubclassesOf(klass)``
        returns all registered subclassess of the given class.
        
        The class registry, subclassing the dictionary type, can be instantiated like
        any other dictionary:
        
          >>> from zope.app.apidoc.classregistry import ClassRegistry
          >>> reg = ClassRegistry()
        
        Let's now add a couple of classes to registry. The classes should implement
        some interfaces, so that we can test all methods on the class registry:
        
          >>> from zope.interface import Interface, implements
        
          >>> class IA(Interface):
          ...      pass
          >>> class IB(IA):
          ...      pass
          >>> class IC(Interface):
          ...      pass
          >>> class ID(Interface):
          ...      pass
        
          >>> class A(object):
          ...    implements(IA)
          >>> reg['A'] = A
        
          >>> class B:
          ...    implements(IB)
          >>> reg['B'] = B
        
          >>> class B2(object):
          ...    implements(IB)
          >>> reg['B2'] = B2
        
          >>> class C(object):
          ...    implements(IC)
          >>> reg['C'] = C
          >>> class A2(A):
          ...    pass
          >>> reg['A2'] = A2
        
        Since the registry is just a dictionary, we can ask for all its keys, which
        are the names of the classes:
        
          >>> names = reg.keys()
          >>> names.sort()
          >>> names
          ['A', 'A2', 'B', 'B2', 'C']
        
          >>> reg['A'] is A
          True
        
        There are two API methods specific to the class registry:
        
        `getClassesThatImplement(iface)`
        --------------------------------
        
        This method returns all classes that implement the specified interface:
        
          >>> from pprint import pprint
          >>> pprint(reg.getClassesThatImplement(IA)) #doctest:+ELLIPSIS
          [('A', <class 'A'>),
           ('B', <class __builtin__.B at ...>),
           ('A2', <class 'A2'>),
           ('B2', <class 'B2'>)]
        
          >>> pprint(reg.getClassesThatImplement(IB)) #doctest:+ELLIPSIS
          [('B', <class __builtin__.B at ...>),
           ('B2', <class 'B2'>)]
        
          >>> pprint(reg.getClassesThatImplement(IC))
          [('C', <class 'C'>)]
        
          >>> pprint(reg.getClassesThatImplement(ID))
          []
        
        `getSubclassesOf(klass)`
        ------------------------
        
        This method will find all classes that inherit the specified class:
        
          >>> pprint(reg.getSubclassesOf(A))
          [('A2', <class 'A2'>)]
        
          >>> pprint(reg.getSubclassesOf(B))
          []
        
        
        Safe Imports
        ------------
        
        Using the ``safe_import()`` we can quickly look up modules by minimizing
        import calls.
        
          >>> from zope.app.apidoc import classregistry
          >>> from zope.app.apidoc.classregistry import safe_import
        
        First we try to find the path in ``sys.modules``, since this lookup is much
        more efficient than importing it. If it was not found, we go back and try
        to import the path. For security reasons, importing new modules is disabled by
        default, unless the global ``__import_unknown_modules__`` variable is set to
        true. If that also fails, we return the `default` value.
        
        Here are some examples::
        
          >>> import sys
          >>> 'zope.app' in sys.modules
          True
        
          >>> safe_import('zope.app') is sys.modules['zope.app']
          True
        
          >>> safe_import('weirdname') is None
          True
        
        For this example, we'll create a dummy module:
        
          >>> import os
          >>> import tempfile
          >>> dir = tempfile.mkdtemp()
          >>> filename = os.path.join(dir, 'testmodule.py')
          >>> sys.path.insert(0, dir)
          >>> f = open(filename, 'w')
          >>> f.write('# dummy module\n')
          >>> f.close()
        
        The temporary module is not already imported:
        
          >>> module_name = 'testmodule'
          >>> module_name in sys.modules
          False
        
        When we try ``safe_import()`` now, we will still get the `default` value,
        because importing new modules is disabled by default:
        
          >>> safe_import(module_name) is None
          True
        
        But once we activate the ``__import_unknown_modules__`` hook, the module
        should be imported:
        
          >>> classregistry.__import_unknown_modules__ = True
        
          >>> safe_import(module_name).__name__ == module_name
          True
          >>> module_name in sys.modules
          True
        
        Now clean up the temporary module, just to play nice:
        
          >>> del sys.modules[module_name]
        
        Importing some code we cannot control, such as twisted, might raise errors
        when imported without having a certain environment. In those cases, the safe
        import should prevent the error from penetrating:
        
          >>> open(os.path.join(dir, 'alwaysfail.py'), 'w').write('raise ValueError\n')
          >>> sys.path.insert(0, dir)
        
          >>> safe_import('alwaysfail') is None
          True
        
        Let's clean up the python path and temporary files:
        
          >>> del sys.path[0]
          >>> import shutil
          >>> shutil.rmtree(dir)
        
        Another method to explicitely turning off the import of certain modules is to
        declare that they should be ignored. For example, if we tell the class
        registry to ignore ``zope.app``,
        
          >>> classregistry.IGNORE_MODULES.append('zope.app')
        
        then we cannot import it anymore, even though we know it is available:
        
          >>> safe_import('zope.app') is None
          True
        
        Note that all sub-packages are also unavailable:
        
          >>> safe_import('zope.app.apidoc') is None
          True
        
        We also need to play nice concerning variables and have to reset the module
        globals:
        
          >>> classregistry.IGNORE_MODULES.pop()
          'zope.app'
          >>> classregistry.__import_unknown_modules__ = False
        
        
        =======
        CHANGES
        =======
        
        3.7.5 (2010-09-12)
        ------------------
        
        - Define ``__file__`` in doctests to make them pass under Python 2.4.
        
        3.7.4 (2010-09-01)
        ------------------
        
        - Prefer the standard libraries doctest module to the one from zope.testing.
        
        - Remove unneeded dependencies zope.app.component and zope.app.container
        
        3.7.3 (2010-07-14)
        ------------------
        
        - Apply refactoring from #153309.
        - Fix LP bug 605057: ZCML links were no longer working (Guilherme Salgado)
        
        3.7.2 (2010-03-07)
        ------------------
        
        - Adapted tests for Python2.4
        
        
        3.7.1 (2010-01-05)
        ------------------
        
        - Updated tests to work with zope.publisher 3.12 (using zope.login).
        
        3.7.0 (2009-12-22)
        ------------------
        
        - Updated tests to work with latest zope.testing and use zope.browserpage in
          favor of zope.app.pagetemplate.
        
        3.6.8 (2009-11-18)
        ------------------
        
        - Updated the tests after moving IPossibleSite and ISite to zope.component.
        
        3.6.7 (2009-09-29)
        ------------------
        
        - Updated the tests after moving ITraverser back to zope.traversing.
        
        3.6.6 (2009-09-15)
        ------------------
        
        - Made the tests work again with the most recent Zope Toolkit KGS.
        
        3.6.5 (2009-07-24)
        ------------------
        
        - Update documentation file in ``zope.site`` from ``README.txt`` to
          ``site.txt``.
        
        3.6.4 (2009-07-23)
        ------------------
        
        - The ``IContained`` interface moved to ``zope.location.interfaces``. Make a
          test pass.
        
        3.6.3 (2009-05-16)
        ------------------
        
        - Explicitly defined default views.
        
        - Replace relative url links with absolute ones.
        
        - Added ``z3c`` packages to the code browser.
        
        - Made `bin/static-apidoc` principially working (publisher and
          webserver mode). There are still some files which are not correctly
          fetched.
        
        3.6.2 (2009-03-17)
        ------------------
        
        - Adapt principal registry book chapter to a new place, as it was moved
          from zope.app.security to zope.principalregistry.
        
        - Remove zcml slugs and old zpkg-related files.
        
        3.6.1 (2009-02-04)
        ------------------
        
        - When a module provides an interface or has an __all__ attribute,
          use one of those for the module documentation.  Fixes LP #323375.
        
        - Undid broken link to `savepoint.txt` caused in 3.6.0.  The latest
          version of the transaction package puts savepoint.txt in the `tests`
          subpackage.
        
        - Expanded the presentation of module documentation.
        
        - Class documentation now includes constructor information.
        
        3.6.0 (2009-01-31)
        ------------------
        
        - Use zope.container instead of zope.app.container.
        
        - Use zope.site instead of zope.app.component and zope.app.folder (in
          at least a few places).
        
        - `savepoint.txt` moved from ZODB's test directory a level up -- we
          follow.
        
        - Make compatible with new zope.traversing and zope.location.
        
        3.5.0 (2009-01-17)
        ------------------
        
        - Adapted transaction book chapters for new transaction egg. The
          README.txt was removed and savepoint.txt was moved. Also add chapter
          about dooming transactions (doom.txt).
        
        - Changed mailing list address to zope-dev at zope.org, because zope3-dev
          is retired now.
        
        - Cleaned up dependencies.
        
        3.4.3 (2007-11-10)
        ------------------
        
        - Fix https://bugs.launchpad.net/zope3/+bug/161737: Misleading text in
          the interface viewer.
        
        - Fix https://bugs.launchpad.net/zope3/+bug/161190: The zope3-dev
          mailinglist has been retired, point to zope-dev.
        
        
        3.4.2 (2007-10-30)
        ------------------
        
        - Avoid deprecation warnings for ``ZopeMessageFactory``.
        
        3.4.1 (2007-10-23)
        ------------------
        
        - Avoid deprecation warnings.
        
        3.4.0 (2007-10-10)
        ------------------
        
        - Improved package meta-data.
        
        - Fixed the code to at least gracefully ignore unzipped eggs. Eventually we
          want to handle eggs well.
        
        3.4.0a1 (2007-04-22)
        --------------------
        
        - Initial release independent of the main Zope tree.
        
Keywords: zope3 api documentation
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Zope Public License
Classifier: Programming Language :: Python
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Framework :: Zope3
