[Zope3-checkins] CVS: Zope3/src/zope/products/apidoc/ifacemodule -
__init__.py:1.1 browser.py:1.1 configure.zcml:1.1
index.pt:1.1 menu.pt:1.1 menu.py:1.1 tests.py:1.1
Stephan Richter
srichter at cosmos.phy.tufts.edu
Thu Jan 29 12:51:18 EST 2004
Update of /cvs-repository/Zope3/src/zope/products/apidoc/ifacemodule
In directory cvs.zope.org:/tmp/cvs-serv11915/apidoc/ifacemodule
Added Files:
__init__.py browser.py configure.zcml index.pt menu.pt menu.py
tests.py
Log Message:
Here comes the new Zope 3 API Documentation tool. You can access it via
http://localhost:8080/++apidoc++/
There is really not much more to say here. Check it out and let me know what
you think.
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/__init__.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface Documentation Module
The interface documentation module retrieves its information from the
interface service. Therefore, currently there are no unregsitered interfaces
listed in the documentation. This might be good, since unregistered interfaces
are usually private and not of interest to a general developer.
$Id: __init__.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
from zope.app import zapi
from zope.interface import implements
from zope.products.apidoc.interfaces import IDocumentationModule
from zope.products.apidoc.utilities import ReadContainerBase
from zope.app.location import LocationProxy
class IInterfaceModule(IDocumentationModule):
"""Interface API Documentation Module
This is a marker interface, so that we can write adapters for objects
implementing this interface.
"""
class InterfaceModule(ReadContainerBase):
r"""Represent the Documentation of all Interfaces.
This documentation is implemented using a simply 'IReadContainer'. The
items of the container are all the interfaces listed in the closest
interface service and above.
Demonstration::
>>> from zope.products.apidoc.ifacemodule import tests
>>> tests.setUp()
>>> module = InterfaceModule()
>>> module.get('IInterfaceModule').getName()
'IInterfaceModule'
>>> id = 'zope.products.apidoc.interfaces.IDocumentationModule'
>>> module.get(id).getName()
'IDocumentationModule'
Here we find an interface that is not in the interface service, but
exists.
>>> module.get('zope.app.interfaces.content.IContentType').getName()
'IContentType'
>>> print '\n'.join([id for id, iface in module.items()])
IInterfaceModule
zope.products.apidoc.interfaces.IDocumentationModule
>>> tests.tearDown()
"""
implements(IInterfaceModule)
# See zope.products.apidoc.interfaces.IDocumentationModule
title = 'Interfaces'
# See zope.products.apidoc.interfaces.IDocumentationModule
description = """
All used and important interfaces are registered through the interface
service. While it would be possible to just list all attributes, it is
hard on the user to read such an overfull list. Therefore, interfaces that
have partial common module paths are bound together.
The documentation of an interface also provides a wide variety of
information, including of course the declared attributes/fields and
methods, but also available adapters, services and utilities that provide
this interface.
"""
def get(self, key, default=None):
"""See zope.app.interfaces.container.IReadContainer"""
service = zapi.getService(self, 'Interfaces')
iface = service.queryInterface(key, default)
if iface is default:
# Yeah, we find more items than we claim to have! This way we can
# handle all interfaces using this module. :-)
parts = key.split('.')
try:
mod = __import__('.'.join(parts[:-1]), {}, {}, ('*',))
except ImportError:
iface = default
else:
iface = getattr(mod, parts[-1], default)
if not iface is default:
iface = LocationProxy(iface, self, key)
return iface
def items(self):
"""See zope.app.interfaces.container.IReadContainer"""
service = zapi.getService(self, 'Interfaces')
items = list(service.items())
items.sort()
items = [(i[0], LocationProxy(i[1], self, i[0])) for i in items]
return items
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/browser.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface Details View
$Id: browser.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
from zope.app import zapi
from zope.component import ComponentLookupError
from zope.interface.declarations import providedBy, directlyProvidedBy
from zope.interface.interfaces import IMethod, IAttribute, IInterface
from zope.products.apidoc.utilities import getPythonPath, stx2html
from zope.proxy import removeAllProxies
from zope.schema.interfaces import IField
def _get(iface, type):
"""Return a dictionary containing all the Fields in a schema."""
iface = removeAllProxies(iface)
items = {}
for name in iface:
attr = iface[name]
if type.isImplementedBy(attr):
items[name] = attr
return items
def _getInOrder(iface, type,
_itemsorter=lambda x, y: cmp(x[1].order, y[1].order)):
"""Return a list of (name, value) tuples in native schema order."""
items = _get(iface, type).items()
items.sort(_itemsorter)
return items
def _getFieldInterface(field):
"""Return PT-friendly dict about the field's interface."""
field = removeAllProxies(field)
# This is bad, but due to bootstrapping, directlyProvidedBy does
# not work
name = field.__class__.__name__
ifaces = list(providedBy(field))
for iface in ifaces:
if iface.getName() == 'I' + name:
return {'name': iface.getName(), 'id': getPythonPath(iface)}
# Giving up...
return {'name': ifaces[0].getName(), 'id': getPythonPath(ifaces[0])}
def _getRequired(field):
"""Return a string representation of whether the field is required."""
if field.required:
return 'required'
else:
return 'optional'
class InterfaceDetails(object):
"""View class for an Interface."""
def __init__(self, context, request):
self.context = context
self.request = request
def getId(self):
"""Return the id of the field as it is defined in the interface
service."""
return zapi.name(self.context)
def getDoc(self):
"""Return the main documentation string of the interface."""
return stx2html(self.context.getDoc())
def getBases(self):
"""Get all bases of this class"""
return [getPythonPath(base) for base in self.context.__bases__]
def getTypes(self):
"""Return a list of interface types that are specified for this
interface.
Note that you should only expect one type at a time."""
context = removeAllProxies(self.context)
types = list(providedBy(context))
types.remove(IInterface)
return [{'name': type.getName(),
'path': getPythonPath(type)}
for type in types]
def getAttributes(self):
"""Return a list of attributes in the order they were specified."""
iface = removeAllProxies(self.context)
attrs = []
for name in iface:
attr = iface[name]
if not IMethod.isImplementedBy(attr) and \
not IField.isImplementedBy(attr):
attrs.append(attr)
return [{'name': attr.getName(),
'doc': stx2html(attr.getDoc() or '', 3)}
for attr in attrs]
def getMethods(self):
"""Return a list of methods in the order they were specified."""
methods = []
return [{'name': method.getName(),
'signature': method.getSignatureString(),
'doc': stx2html(method.getDoc() or '', 3)}
for method in _get(self.context, IMethod).values()]
def getFields(self):
"""Return a list of fields in the order they were specified."""
fields = map(lambda x: x[1], _getInOrder(self.context, IField))
return [{'name': field.getName(),
'iface': _getFieldInterface(field),
'required': _getRequired(field),
'default': field.default.__repr__,
'description': field.description
}
for field in fields]
def getRequiredAdapters(self):
"""Get adapters where this interface is required."""
service = zapi.getService(self.context, 'Adapters')
context = removeAllProxies(self.context)
adapters = []
for adapter in service.getRegisteredMatching(required=context):
adapters.append({
'provided': getPythonPath(adapter[1]),
'required': [getPythonPath(iface) for iface in adapter[2]],
'name': adapter[3],
'factory': getPythonPath(adapter[4][0])
})
return adapters
def getProvidedAdapters(self):
"""Get adapters where this interface is provided."""
service = zapi.getService(self.context, 'Adapters')
context = removeAllProxies(self.context)
adapters = []
for adapter in service.getRegisteredMatching(provided=context):
adapters.append({
'required': [getPythonPath(iface)
for iface in adapter[2]+(adapter[0],)],
'name': adapter[3],
'factory': getPythonPath(adapter[4][0])
})
return adapters
def getFactories(self):
"""Return the factories, who will provide objects implementing this
interface."""
service = zapi.getService(self.context, 'Factories')
try:
factories = service.getFactoriesFor(removeAllProxies(self.context))
except ComponentLookupError:
return []
return [{'name': n,
'factory': f,
'title': service.getFactoryInfo(n).title
} for n, f in factories]
def getUtilities(self):
"""Return all utilities that provide this interface."""
service = zapi.getService(self.context, 'Utilities')
utils = service.getUtilitiesFor(removeAllProxies(self.context))
return [{'name': util[0],
'path': getPythonPath(util[1].__class__)} for util in utils]
def getServices(self):
"""Return all services (at most one) that provide this interface."""
iface = removeAllProxies(self.context)
service = zapi.getService(self.context, 'Services')
services = service.getServiceDefinitions()
services = filter(lambda x: x[1] is iface, services)
return [ser[0] for ser in services]
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/configure.zcml ===
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<class class=".InterfaceModule">
<allow interface=".IInterfaceModule" />
<allow interface="zope.app.interfaces.container.IReadContainer" />
</class>
<!-- Register the module with the API Documentation System -->
<utility
provides="zope.products.apidoc.interfaces.IDocumentationModule"
factory=".InterfaceModule"
name="Interface" />
<!-- The name for the interface content cannot be 'index.html', since the
introspector uses this name already. -->
<browser:page
for="zope.interface.interfaces.IInterface"
permission="zope.View"
class=".browser.InterfaceDetails"
name="apiindex.html"
template="index.pt" />
<!-- Interface Documentation Module Menu -->
<adapter
provides="zope.products.statictree.interfaces.IChildObjects"
for=".IInterfaceModule"
factory=".menu.InterfaceModuleChildObjects" />
<browser:page
for=".InterfaceModule"
permission="zope.View"
class=".menu.Menu"
name="menu.html"
template="menu.pt" />
</configure>
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/index.pt ===
<html metal:use-macro="views/apidoc_macros/details">
<body metal:fill-slot="contents">
<h1 class="details-header" tal:content="view/getId">
zope.app.interfaces.IInterface
</h1>
<h3 tal:condition="view/getTypes">
Type:
<tal:omit-tag tal:repeat="type view/getTypes" >
<a href=""
tal:attributes="href string:../${type/path}/apiindex.html"
tal:content="type/name"/><tal:block
condition="not:repeat/type/end">, </tal:block>
</tal:omit-tag>
</h3>
<div class="indent">
<div class="documentation" tal:content="structure view/getDoc">
Here is the doc string
</div>
</div>
<h2 class="details-section">Bases</h2>
<div class="indent"
tal:define="bases view/getBases">
<ul class="attr-list" tal:condition="bases">
<li tal:repeat="base bases">
<a href=""
tal:attributes="href string:../$base/apiindex.html"
tal:content="base" />
</li>
</ul>
<p tal:condition="not: bases">
<em>There are no base classes.</em>
</p>
</div>
<h2 class="details-section">Attributes/Fields</h2>
<div class="indent"
tal:define="attributes view/getAttributes;
fields view/getFields">
<ul class="attr-list"
tal:condition="python: attributes or fields">
<li tal:repeat="attr attributes">
<b><code tal:content="attr/name">attr</code></b> (Attribute)<br>
<div class="inline-documentation" tal:content="structure attr/doc">
attr desc
</div>
</li>
<li tal:repeat="field fields">
<b><code tal:content="field/name">field</code></b>
- <a href=""
tal:attributes="href string:../${field/iface/id}/apiindex.html">
<code tal:content="field/iface/name">IField</code></a>
(<span tal:content="string:${field/required}, ">optional, </span>
default = <code tal:content="field/default" />)<br>
<span tal:content="field/description">field desc</span>
</li>
</ul>
<p tal:condition="python: not (attributes or fields)">
<em>There are no attributes or fields specified.</em>
</p>
</div>
<h2 class="details-section">Methods</h2>
<div class="indent">
<ul class="attr-list" tal:condition="view/getMethods">
<li tal:repeat="method view/getMethods">
<b><code
tal:content="string:${method/name}${method/signature}" />
</b><br>
<div class="inline-documentation" tal:content="structure method/doc">
method desc
</div>
</li>
</ul>
<p tal:condition="not: view/getMethods">
<em>There are no methods or fields specified.</em>
</p>
</div>
<h2 class="details-section">Adapters</h2>
<div class="indent"
tal:define="adapters view/getRequiredAdapters"
tal:condition="adapters">
<h3>Adapters where this interface is required:</h3>
<ul class="attr-list">
<li tal:repeat="adapter adapters">
<b><code tal:content="adapter/factory" /></b>
<span tal:condition="adapter/name">
(name: <tal:block content="adapter/name" />)
</span>
<br />
<i>provides:</i>
<a href=""
tal:attributes="href string:../${adapter/provided}/apiindex.html"
tal:content="adapter/provided" /><br />
<tal:block condition="adapter/required">
<i>also required:</i>
<tal:block repeat="iface adapter/required">
<a href=""
tal:attributes="href string:../$iface/apiindex.html"
tal:content="iface" /><tal:block
condition="not:repeat/iface/end">, </tal:block>
</tal:block>
</tal:block>
</li>
</ul>
</div>
<div class="indent"
tal:define="adapters view/getProvidedAdapters"
tal:condition="adapters">
<h3>Adapters that provide this interface:</h3>
<ul>
<li tal:repeat="adapter adapters">
<b><code tal:content="adapter/factory" /></b>
<span tal:condition="adapter/name">
(name: <tal:block content="adapter/name" />)
</span>
<br />
<i>requires:</i>
<tal:block repeat="iface adapter/required">
<a href=""
tal:condition="iface"
tal:attributes="href string:../$iface/"
tal:content="iface" /><tal:block
condition="not:repeat/iface/end">, </tal:block>
<span tal:condition="not: iface">
No interface required.
</span>
</tal:block>
</li>
</ul>
</div>
<p tal:condition="
python: not (view.getRequiredAdapters() or view.getProvidedAdapters())">
<em>There are no adapters registered for this interface.</em>
</p>
<div tal:define="factories view/getFactories;
utilities view/getUtilities;
services view/getServices"
tal:condition="python: factories or utilities or services">
<h2 class="details-section">Other Information</h2>
<div class="indent">
<tal:block condition="factories">
<h3 class="details-section">Factories</h3>
<div class="indent">
<div class="small">
A list of factories that create objects implement this interface.
</div>
<ul>
<li tal:repeat="factory factories">
<a href=""
tal:attributes="href string:../../Factory/${factory/name}/"
tal:content="factory/name" />
<tal:block replace="string:(${factory/title})"
condition="factory/title" />
</li>
</ul>
</div>
</tal:block>
<tal:block condition="utilities">
<h3 class="details-section">Utilities</h3>
<div class="indent">
<div class="small">
A list of utilities that are are registered to provide this
interface.
</div>
<ul>
<li tal:repeat="utility utilities">
<a href=""
tal:attributes="href string:../../Utility/${utility/path}/"
tal:content="utility/path" />
<tal:block replace="string: (name: ${utility/name})"
condition="utility/name" />
</li>
</ul>
</div>
</tal:block>
<tal:block condition="services">
<h3 class="details-section">Service</h3>
<div class="indent">
<div class="small">
A list of services (usually just one) that are are registered to
provide this interface.
</div>
<ul>
<li tal:repeat="service services">
<a href=""
tal:attributes="href string:../../Service/$service/"
tal:content="service" />
</li>
</ul>
</div>
</tal:block>
</div>
</div>
</body>
</html>
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/menu.pt ===
<html metal:use-macro="views/apidoc_macros/menu">
<body>
<p metal:fill-slot="post_menu" class="small">
Note: These are only interfaces that are registered with the Interface
Service.
</p>
</body>
</html>
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/menu.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Interface Module Browser Menu (Tree)
A list of interfaces from the Interface Service is pretty much unmanagable and
is very confusing. Therefore it is nice to split the path of the interface, so
that we get a deeper tree with nodes having shorter, manageable names.
$Id: menu.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
from zope.app import zapi
from zope.interface import implements
from zope.app.interfaces.location import ILocation
from zope.app.location import LocationProxy
from zope.products.statictree.interfaces import IChildObjects
from zope.products.apidoc.ifacemodule import IInterfaceModule
from zope.products.apidoc.utilities import ReadContainerBase
from zope.proxy import removeAllProxies
class IModule(ILocation):
"""Represents some module
Marker interface, so that we can register an adapter for it."""
class Module(ReadContainerBase):
r"""Represents a Python module
Examples: zope, zope.app, zope.app.interfaces
However, we usually use it only for the last case.
Usage::
>>> from zope.products.apidoc.ifacemodule import tests
>>> from zope.products.apidoc.ifacemodule import InterfaceModule
>>> tests.setUp()
>>> module = Module(InterfaceModule(), 'zope.products')
>>> module.get('apidoc.interfaces.IDocumentationModule').getName()
'IDocumentationModule'
>>> module.get(
... 'zope.products.apidoc.interfaces.IDocumentationModule') is None
True
>>> print '\n'.join([id for id, iface in module.items()])
zope.products.apidoc.interfaces.IDocumentationModule
>>> tests.tearDown()
"""
implements(IModule)
def __init__(self, parent, name):
self.__parent__ = parent
self.__name__ = name
def get(self, key, default=None):
name = self.__name__ + '.' + key
return self.__parent__.get(name, default)
def items(self):
parent = self.__parent__
items = []
for key in parent.keys():
if key.startswith(self.__name__):
items.append((key, LocationProxy(parent[key], self, key)))
return items
class InterfaceModuleChildObjects:
r"""Module Adapter for Static Tree
This adapter is used when building a static tree for the browser.
Functionality::
>>> from zope.products.apidoc.ifacemodule import tests
>>> from zope.products.apidoc.ifacemodule import InterfaceModule
>>> tests.setUp()
>>> module = InterfaceModule()
>>> module = tests.rootLocation(module, 'Interface')
>>> adapter = InterfaceModuleChildObjects(module)
>>> adapter.hasChildren()
True
>>> print '\n'.join([c.__name__ for c in adapter.getChildObjects()])
IInterfaceModule
zope.products.apidoc.interfaces
>>> tests.tearDown()
"""
implements(IChildObjects)
__used_for__ = IInterfaceModule
def __init__(self, context):
self.context = context
def hasChildren(self):
"""See zope.products.statictree.interfaces.IChildObject"""
return bool(len(self.context))
def getChildObjects(self):
"""See zope.products.statictree.interfaces.IChildObject"""
objects = {}
names = removeAllProxies(self.context.keys())
names.sort()
for name in names:
# Split these long names and make part of the module path separate
# entries. Currently we only split by the term '.interfaces', but
# a more sophisticated algorithm could be developed.
iface_loc = name.find('.interfaces')
if iface_loc == -1:
objects[name] = LocationProxy(self.context[name],
self.context, name)
else:
module_name = name[:iface_loc+11]
objects[module_name] = Module(self.context, module_name)
items = objects.items()
items.sort()
return [x[1] for x in items]
class Menu(object):
"""Menu View Helper Class
A class that helps building the menu. The menu_macros expects the menu view
class to have the getMenuTitle(node) and getMenuLink(node) methods
implemented. 'node' is a 'zope.products.statictree.node.Node' instance.
"""
def getMenuTitle(self, node):
"""Return the title of the node that is displayed in the menu."""
if isinstance(removeAllProxies(node.context.__parent__), Module):
parent = node.context.__parent__
return zapi.name(node.context).replace(zapi.name(parent)+'.', '')
return zapi.name(node.context)
def getMenuLink(self, node):
"""Return the HTML link of the node that is displayed in the menu."""
if isinstance(removeAllProxies(node.context), Module):
return None
return './' + zapi.name(node.context) + '/apiindex.html'
=== Added File Zope3/src/zope/products/apidoc/ifacemodule/tests.py ===
##############################################################################
#
# Copyright (c) 2004 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Tests for the Interface Documentation Module
$Id: tests.py,v 1.1 2004/01/29 17:51:16 srichter Exp $
"""
import unittest
from zope.app import zapi
from zope.app.interfaces.traversing import IContainmentRoot
from zope.app.location import LocationProxy
from zope.app.tests import placelesssetup
from zope.interface import implements
from zope.products.apidoc.ifacemodule import IInterfaceModule
from zope.products.apidoc.interfaces import IDocumentationModule
from zope.testing.doctestunit import DocTestSuite
def setUp():
placelesssetup.setUp()
service = zapi.getService(None, 'Interfaces')
service.provideInterface(None, IDocumentationModule)
service.provideInterface('IInterfaceModule', IInterfaceModule)
def tearDown():
placelesssetup.tearDown()
class Root:
implements(IContainmentRoot)
__parent__ = None
__name__ = ''
def rootLocation(obj, name):
return LocationProxy(obj, Root(), name)
def test_suite():
return unittest.TestSuite((
DocTestSuite('zope.products.apidoc.ifacemodule'),
DocTestSuite('zope.products.apidoc.ifacemodule.menu'),
))
if __name__ == '__main__':
unittest.main()
More information about the Zope3-Checkins
mailing list