[Zope3-checkins] CVS: Zope3/src/zope/products/apidoc -
MAINTAINER.txt:1.1 README.txt:1.1 TODO.txt:1.1
__init__.py:1.1 configure.zcml:1.1 interfaces.py:1.1
tests.py:1.1 utilities.py:1.1 version.txt:1.1
Stephan Richter
srichter at cosmos.phy.tufts.edu
Thu Jan 29 12:51:13 EST 2004
Update of /cvs-repository/Zope3/src/zope/products/apidoc
In directory cvs.zope.org:/tmp/cvs-serv11915/apidoc
Added Files:
MAINTAINER.txt README.txt TODO.txt __init__.py configure.zcml
interfaces.py tests.py utilities.py version.txt
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/MAINTAINER.txt ===
Stephan Richter
Email: stephan.richter at tufts.edu
IRC nick: srichter
=== Added File Zope3/src/zope/products/apidoc/README.txt ===
Zope 3 API Documentation
========================
This Zope 3 product provides a fully dynamic API documentation of Zope 3 and
registered add-on components. The product is very extensible and can be easily
extended by implementing new modules.
Installation
------------
1. In your 'products.zcml' file make sure that
(a) the static tree product is registered using::
<include package="zope.products.apidoc"/>
(b) you register this product using::
<include package="zope.products.apidoc"/>
2. Restart Zope 3.
3. You can now access the documentation via the new namespace "++apidoc++",
like in::
http://localhost:8080/++apidoc++/
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.products.apidoc.interfaces.IDocumentationModule"
factory=".examplemodule.ExampleModule"
name="Example" />
3. Take care of security by allowing at least 'IDocumentationModule':
<class class=".ExampleModule">
<allow interface="zope.products.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.
=== Added File Zope3/src/zope/products/apidoc/TODO.txt ===
TO DO
=====
- better README.txt
- Refactor some of the views, so that templates can be reused
- move out StructuredText (???)
=== Added File Zope3/src/zope/products/apidoc/__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.
#
##############################################################################
"""Zope 3 API Documentation
$Id: __init__.py,v 1.1 2004/01/29 17:51:12 srichter Exp $
"""
from interfaces import IDocumentationModule
from zope.app import zapi
from zope.app.interfaces.container import IReadContainer
from zope.app.location import locate
from zope.app.interfaces.location import ILocation
from zope.interface import implements
from zope.products.apidoc.utilities import ReadContainerBase
class APIDocumentation(ReadContainerBase):
r"""Represent the complete API Documentation.
This documentation is implemented using a simply 'IReadContainer'. The
items of the container are all registered utilities for
IDocumentationModule.
Demonstration::
>>> from zope.products.apidoc import tests
>>> tests.setUp()
>>> doc = APIDocumentation(None, '++apidoc++')
>>> doc.get('ZCML').title
'ZCML Reference'
>>> doc.get('Documentation') is None
True
>>> print '\n'.join([id for id, mod in doc.items()])
Interface
ZCML
>>> tests.tearDown()
"""
implements(ILocation)
def __init__(self, parent, name):
self.__parent__ = parent
self.__name__ = name
def get(self, key, default=None):
"""See zope.app.interfaces.container.IReadContainer"""
utility = zapi.queryUtility(self, IDocumentationModule, default, key)
if utility != default:
locate(utility, self, key)
return utility
def items(self):
"""See zope.app.interfaces.container.IReadContainer"""
items = zapi.getUtilitiesFor(self, IDocumentationModule)
items.sort()
utils = []
for key, value in items:
locate(value, self, key)
utils.append((key, value))
return utils
def handleNamespace(name, parameters, pname, ob, request):
"""Used to traverse to an API Documentation."""
return APIDocumentation(ob, pname)
=== Added File Zope3/src/zope/products/apidoc/configure.zcml ===
<configure
xmlns="http://namespaces.zope.org/zope"
i18n_domain="api_doc">
<class class=".APIDocumentation">
<allow interface="zope.app.interfaces.container.IReadContainer" />
</class>
<traversalNamespace
name="apidoc"
handler=".handleNamespace" />
<include package=".browser" />
<!-- API Documentation Modules -->
<include package=".classmodule" />
<include package=".ifacemodule" />
<include package=".servicemodule" />
<include package=".utilitymodule" />
<include package=".viewmodule" />
<include package=".zcmlmodule" />
</configure>
=== Added File Zope3/src/zope/products/apidoc/interfaces.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.
#
##############################################################################
"""Generic API Documentation Interfaces
$Id: interfaces.py,v 1.1 2004/01/29 17:51:12 srichter Exp $
"""
from zope.interface import Interface
from zope.schema import TextLine, Text
class IDocumentationModule(Interface):
"""Zope 3 API Documentation Module
A documentation module contains the documentation for one specific aspect
of the framework, such as ZCML directives or interfaces.
The interface is used to register module as utilitites.
"""
title = TextLine(
title=u"Title",
description=u"The title of the documentation module.",
required=True)
description = Text(
title=u"Module Description",
description=u"This text describes the functionality of the module.",
required=True)
=== Added File Zope3/src/zope/products/apidoc/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:12 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.interfaces import IDocumentationModule
from zope.products.apidoc.ifacemodule import InterfaceModule
from zope.products.apidoc.zcmlmodule import ZCMLModule
from zope.testing.doctestunit import DocTestSuite
def setUp():
placelesssetup.setUp()
service = zapi.getService(None, 'Utilities')
service.provideUtility(IDocumentationModule, InterfaceModule(), 'Interface')
service.provideUtility(IDocumentationModule, ZCMLModule(), 'ZCML')
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'),
DocTestSuite('zope.products.apidoc.utilities'),
))
if __name__ == '__main__':
unittest.main()
=== Added File Zope3/src/zope/products/apidoc/utilities.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.
#
##############################################################################
"""Utilties to make the life of Documentation Modules easier.
$Id: utilities.py,v 1.1 2004/01/29 17:51:12 srichter Exp $
"""
import re
import types
import inspect
import docutils
from zope.app.interfaces.container import IReadContainer
from zope.app.renderer.rest import Writer
from zope.interface import implements, implementedBy
from zope.products.apidoc.StructuredText import HTML
from zope.security.checker import getCheckerForInstancesOf
from zope.security.interfaces import INameBasedChecker
_remove_html_overhead = re.compile(
r'(?sm)^<html.*<body.*?>\n(.*)</body>\n</html>\n')
_marker = object()
class ReadContainerBase(object):
"""Base for IReadContainer objects.
This is a base class that minimizes the implementation of IReadContainers
to two methods, get() and items(), since the other methods can be
implemented using these two.
Demonstration::
Make a sample implementation first
>>> class Container(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
>>> container.get('a')
1
>>> container.get('c') is None
True
>>> container['b']
2
>>> container.items()
[('a', 1), ('b', 2)]
>>> container.keys()
['a', 'b']
>>> container.values()
[1, 2]
>>> 1 in container
True
>>> len(container)
2
"""
implements(IReadContainer)
def get(self, key, default=None):
raise NotImplemented
def items(self):
raise NotImplemented
def __getitem__(self, key):
default = object()
obj = self.get(key, default)
if obj is default:
raise KeyError, key
return obj
def __contains__(self, key):
return self.get(key) is None
def keys(self):
return map(lambda x: x[0], self.items())
def __iter__(self):
return self.values().__iter__()
def values(self):
return map(lambda x: x[1], self.items())
def __len__(self):
return len(self.items())
def getPythonPath(obj):
"""Return the path of the object in standard Python notation.
This method makes only sense for classes and interfaces. Instances do not
have a '__name__' attribute, so we would expect them to fail.
Example::
>>> from zope.interface import Interface
>>> class ISample(Interface):
... pass
>>> class Sample(object):
... pass
>>> getPythonPath(ISample)
'zope.products.apidoc.utilities.ISample'
>>> getPythonPath(Sample)
'zope.products.apidoc.utilities.Sample'
>>> try:
... getPythonPath(Sample())
... except AttributeError:
... print 'failed'
failed
"""
if obj is None:
return None
module = obj.__module__
return '%s.%s' %(module, obj.__name__)
def stx2html(text, level=1):
r"""Convert STX text to HTML.
Example::
>>> text = 'Header\n\n Normal text goes here.'
>>> stx2html(text)
'<h1>Header</h1>\n<p> Normal text goes here.</p>\n'
>>> stx2html(text, level=3)
'<h3>Header</h3>\n<p> Normal text goes here.</p>\n'
>>> stx2html(text, level=6)
'<h6>Header</h6>\n<p> Normal text goes here.</p>\n'
"""
html = HTML(text, level)
html = _remove_html_overhead.sub(r'\1', html)
return html
def getPermissionIds(name, checker=_marker, klass=_marker):
"""Get the permissions of an attribute.
Either the klass or the checker 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.
Example::
We first define the class and then the checker for it
>>> from zope.security.checker import Checker, defineChecker
>>> class Sample(object):
... attr = 'value'
>>> class Sample2(object):
... pass
>>> checker = Checker({'attr': 'zope.Read'}.get,
... {'attr': 'zope.Write'}.get)
>>> defineChecker(Sample, checker)
Now let's see how this function works
>>> entries = getPermissionIds('attr', klass=Sample)
>>> entries['read_perm']
'zope.Read'
>>> entries['write_perm']
'zope.Write'
>>> entries = getPermissionIds('attr', getCheckerForInstancesOf(Sample))
>>> entries['read_perm']
'zope.Read'
>>> entries['write_perm']
'zope.Write'
>>> entries = getPermissionIds('attr2', klass=Sample)
>>> print entries['read_perm']
N/A
>>> print entries['write_perm']
N/A
>>> entries = getPermissionIds('attr', klass=Sample2)
>>> entries['read_perm'] is None
True
>>> print entries['write_perm'] is None
True
"""
assert (klass is _marker) != (checker is _marker)
entry = {}
if klass is not _marker:
checker = getCheckerForInstancesOf(klass)
if checker is not None and \
INameBasedChecker.isImplementedBy(checker):
entry['read_perm'] = checker.permission_id(name) or 'N/A'
entry['write_perm'] = checker.setattr_permission_id(name) or 'N/A'
else:
entry['read_perm'] = entry['write_perm'] = None
return entry
def getFunctionSignature(func):
"""Return the signature of a function or method.
The 'func' argument *must* be a generic function or a method of a class.
Examples::
>>> def func(attr, attr2=None):
... pass
>>> print getFunctionSignature(func)
(attr, attr2=None)
>>> def func(attr, **kw):
... pass
>>> print getFunctionSignature(func)
(attr, **kw)
>>> def func(attr, attr2=None, **kw):
... pass
>>> print getFunctionSignature(func)
(attr, attr2=None, **kw)
>>> def func(*args, **kw):
... pass
>>> print getFunctionSignature(func)
(*args, **kw)
>>> def func(**kw):
... pass
>>> print getFunctionSignature(func)
(**kw)
>>> class Klass(object):
... def func(self, attr):
... pass
>>> print getFunctionSignature(Klass.func)
(attr)
>>> class Klass(object):
... def func(self, attr, *args, **kw):
... pass
>>> print getFunctionSignature(Klass.func)
(attr, *args, **kw)
>>> try:
... getFunctionSignature('func')
... except AssertionError:
... print 'Argument not a function or method.'
Argument not a function or method.
"""
assert type(func) in (types.FunctionType, types.MethodType)
args, varargs, varkw, default = inspect.getargspec(func)
placeholder = object()
sig = '('
# By filling up the default tuple, we now have equal indeces for args and
# default.
if default is not None:
default = (placeholder,)*(len(args)-len(default)) + default
else:
default = (placeholder,)*len(args)
str_args = []
for i in range(len(args)):
# Neglect self, since it is always there and not part of the signature.
# This way the implementation and interface signatures should match.
if args[i] == 'self' and type(func) == types.MethodType:
continue
if default[i] is placeholder:
str_args.append(args[i])
else:
str_args.append(args[i] + '=' + default[i].__repr__())
if varargs:
str_args.append('*'+varargs)
if varkw:
str_args.append('**'+varkw)
sig += ', '.join(str_args)
return sig + ')'
def getPublicAttributes(obj):
"""Return a list of public attribute names.
This excludes any attribute starting with '_'. 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.
Examples::
>>> class Sample(object):
... attr = None
... def __str__(self):
... return ''
... def func(self):
... pass
... def _getAttr(self):
... return self.attr
... attr2 = property(_getAttr)
>>> class Sample2:
... attr = None
>>> class Sample3(Sample):
... attr3 = None
>>> attrs = getPublicAttributes(Sample)
>>> attrs.sort()
>>> print attrs
['attr', 'attr2', 'func']
>>> attrs = getPublicAttributes(Sample())
>>> attrs.sort()
>>> print attrs
['attr', 'attr2', 'func']
>>> attrs = getPublicAttributes(Sample2)
>>> attrs.sort()
>>> print attrs
['attr']
>>> attrs = getPublicAttributes(Sample3)
>>> attrs.sort()
>>> print attrs
['attr', 'attr2', 'attr3', 'func']
"""
attrs = []
for attr in dir(obj):
if attr.startswith('_'):
continue
else:
attrs.append(attr)
return attrs
def 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 'interfaces' or 'klass' must be specified. If 'interfaces' is not
specified, the 'klass' is used to retrieve a list of
interfaces. 'interfaces' must be iteratable.
'asPath' specifies whether the dotted name of the interface or the
interface object is returned.
If no match is found, 'None' is returned.
Example::
>>> from zope.interface import Interface, Attribute
>>> class I1(Interface):
... attr = Attribute('attr')
>>> class I2(I1):
... def getAttr():
... '''get attr'''
>>> class Sample(object):
... implements(I2)
>>> getInterfaceForAttribute('attr', (I1, I2), asPath=False).getName()
'I1'
>>> getInterfaceForAttribute('getAttr', (I1, I2), asPath=False).getName()
'I2'
>>> getInterfaceForAttribute('attr', klass=Sample, asPath=False).getName()
'I1'
>>> getInterfaceForAttribute(
... 'getAttr', klass=Sample, asPath=False).getName()
'I2'
>>> getInterfaceForAttribute('attr', (I1, I2))
'zope.products.apidoc.utilities.I1'
>>> getInterfaceForAttribute('attr2', (I1, I2)) is None
True
>>> getInterfaceForAttribute('attr2', klass=Sample) is None
True
>>> try:
... getInterfaceForAttribute('getAttr')
... except AssertionError:
... print 'need to specify the interfaces or a klass'
need to specify the interfaces or a klass
"""
assert (interfaces is _marker) != (klass is _marker)
if interfaces is _marker:
direct_interfaces = list(implementedBy(klass))
interfaces = {}
for interface in direct_interfaces:
interfaces[interface] = 1
for base in interface.getBases():
interfaces[base] = 1
interfaces = interfaces.keys()
for interface in interfaces:
if name in interface.names():
if asPath:
return getPythonPath(interface)
return interface
return None
=== Added File Zope3/src/zope/products/apidoc/version.txt ===
apidoc 0.1
More information about the Zope3-Checkins
mailing list