Code Documentation Module

The code documentation module package

>>> from zope.app.apidoc import codemodule

provides systematic and autogenerated documentation about the content of your Zope 3 related Python packages. The code module can be created like this:

>>> cm = codemodule.codemodule.CodeModule()
>>> cm.getDocString()
u'Zope 3 root.'

This object extends the zope.app.apidoc.codemodule.module.Module class, since it can be seen as some sort of root package. However, its sementacs are obviously a bit different:

>>> cm.getFileName()
''
>>> cm.getPath()
''
>>> cm.isPackage()
True
>>> sorted(cm.keys())
[u'BTrees', u'ZConfig', u'ZODB', u'persistent', u'transaction', ...]

Module

The module.Module class represents a Python module or package in the documentation tree. It can be easily setup by simply passing the parent module, the module name (not the entire Python path) and the Python module instance itself:

>>> import zope.app.apidoc
>>> module = codemodule.module.Module(None, 'apidoc', zope.app.apidoc)

We can now get some of the common module attributes via accessor methods:

>>> module.getDocString() is None
True
>>> fname = module.getFileName()
>>> fname = fname.replace('\\', '/') # Fix for Windows users
>>> 'zope/app/apidoc/__init__.py' in fname
True
>>> module.getPath()
'zope.app.apidoc'
>>> module.isPackage()
True
>>> m2 = codemodule.module.Module(
...     None, 'apidoc', zope.app.apidoc.apidoc)
>>> m2.isPackage()
False

The setup for creating the sub module and class tree is automatically called during initialization, so that the sub-objects are available as soon as you have the object:

>>> keys = module.keys()
>>> 'codemodule' in keys
True
>>> 'meta.zcml' in keys
True
>>> import zope.app.apidoc.browser
>>> print(module['browser'].getPath())
zope.app.apidoc.browser

Now, the module.Module.get() is actually much smarter than you might originally suspect, since it can actually get to more objects than it promises. If a key is not found in the module’s children, it tries to import the key as a module relative to this module.

For example, while tests directories are not added to the module and classes hierarchy (since they do not provide or implement any API), we can still get to them:

>>> import zope.app.apidoc.tests
>>> print(module['tests'].getPath())
zope.app.apidoc.tests
>>> names = sorted(module['tests'].keys())
>>> names
['BrowserTestCase', 'LayerDocFileSuite', 'LayerDocTestSuite', 'Root', ...]

Classes

Setting up a class for documentation is not much harder. You only need to provide an object providing IModule as a parent, the name and the klass itself:

>>> import zope.app.apidoc.apidoc
>>> module = codemodule.module.Module(
...     None, 'apidoc', zope.app.apidoc.apidoc)
>>> klass = codemodule.class_.Class(module, 'APIDocumentation',
...                                 zope.app.apidoc.apidoc.APIDocumentation)

This class provides data about the class in an accessible format. The Python path and doc string are easily retrieved using:

>>> klass.getPath()
'zope.app.apidoc.apidoc.APIDocumentation'

>>> "The collection of all API documentation" in klass.getDocString()
True

A list of base classes can also be retrieved. The list only includes direct bases, so if we have class Blah, which extends Bar, which extends Foo, then the base of Blah is just Bar. In our example this looks like this:

>>> klass.getBases()
(<class 'zope.app.apidoc.utilities.ReadContainerBase'>,)

In the other direction, you can get a list of known subclasses. The list only includes those subclasses that are registered with the global zope.app.apidoc.classregistry.classRegistry dictionary. In our example:

>>> class APIDocSubclass(zope.app.apidoc.apidoc.APIDocumentation):
...   pass
>>> klass2 = codemodule.class_.Class(module, 'APIDocSubclass', APIDocSubclass)
>>> klass.getKnownSubclasses()
[<class 'APIDocSubclass'>]

For a more detailed analysis, you can also retrieve the public attributes and methods of this class:

>>> klass.getAttributes()
[]

>>> klass.getMethods()[0]
('get', <function APIDocumentation.get at ...>,
 <InterfaceClass zope.interface.common.mapping.IReadMapping>)

>>> klass.getConstructor()
<function APIDocumentation.__init__ at ...>

Let’s have a closer look at the class_.Class.getAttributes() method. First we create an interface called IBlah that is implemented by the class Blah:

>>> import zope.interface
>>> class IBlie(zope.interface.Interface):
...      bli = zope.interface.Attribute('Blie')
>>> class IBlah(IBlie):
...      foo = zope.interface.Attribute('Foo')
>>> @zope.interface.implementer(IBlah)
... class Blah(object):
...      foo = 'f'
...      bar = 'b'
...      bli = 'i'
...      _blah = 'l'

The Blah class also implements a public and private attribute that is not listed in the interface. Now we create the class documentation wrapper:

>>> klass = codemodule.class_.Class(module, 'Blah', Blah)
>>> from pprint import pprint
>>> pprint(klass.getAttributes())
[('bar', 'b', None),
 ('bli', 'i', <InterfaceClass __builtin__.IBlie>),
 ('foo', 'f', <InterfaceClass __builtin__.IBlah>)]

So, the function returns a list of tuples of the form (name, value, interface), where the interface is the interface in which the attribute was declared. The interface is None, if the attribute was not declared. Also note that attributes starting with an underscore are ignored.

Let’s now have a look at how methods are looked up returned. So we create a new IBlah interface, this time describing methods, and then its implementation Blah, which has some other additional methods:

>>> class IBlah(zope.interface.Interface):
...      def foo(): pass
>>> @zope.interface.implementer(IBlah)
... class Blah(object):
...
...
...      def foo(self):
...          pass
...      def bar(self):
...          pass
...      def _blah(self):
...          pass

Now we create the class documentation wrapper:

>>> klass = codemodule.class_.Class(module, 'Blah', Blah)

and get the method documentation:

>>> pprint(klass.getMethods())
[('bar', <function Blah.bar at ...>, None),
 ('foo', <function Blah.foo at ...>, <InterfaceClass __builtin__.IBlah>)]

Function

Functions are pretty much documented in the same way as all other code documentation objects and provides a similar API to the classes. A function documentation object is quickly created:

>>> func = codemodule.function.Function(
...     module, 'handleNamespace',
...     zope.app.apidoc.apidoc.handleNamespace)

This class provides data about the function in an accessible format. The Python path, signature and doc string are easily retrieved using:

>>> func.getPath()
'zope.app.apidoc.apidoc.handleNamespace'
>>> func.getSignature()
'(ob, name)'
>>> func.getDocString()
'Used to traverse to an API Documentation.'

For a more detailed analysis, you can also retrieve the attributes of the function

>>> func.getAttributes()
[]

but this function has none as most functions. So let’s create a new function

>>> def foo(bar=1):
...     pass
>>> func = codemodule.function.Function(module, 'foo', foo)

which originally does not have any attributes

>>> func.getAttributes()
[]

but if we add an attribute, it will be listed:

>>> foo.blah = 1
>>> func.getAttributes()
[('blah', 1)]

Text File

Text files represent plain-text documentation files like this one. Once we have a text file documentation object

>>> import os
>>> path = os.path.join(os.path.dirname(codemodule.__file__), 'README.rst')
>>> readme = codemodule.text.TextFile(path, 'README.rst', module)

we can ask it for the content of the file:

>>> "Code Documentation Module" in readme.getContent()
True

ZCML File

ZCML file documentation objects present configuration files and parse the file content to provide some advanced markup. The object is easily instantiated:

>>> path = os.path.join(os.path.dirname(codemodule.__file__),
...                     'configure.zcml')
>>> module = codemodule.module.Module(None, 'zope.app.apidoc.codemodule',
...                                   zope.app.apidoc.codemodule)
>>> zcml = codemodule.zcml.ZCMLFile(path, module, module, 'configure.zcml')

The interesting attribute of the object is the rootElement, since it contains the root XML element and thus the entire XML tree. The rootElement attribute is a lazy property, so that it is not loaded until accessed for the first time:

>>> root = zcml.rootElement
>>> root
<Directive (u'http://namespaces.zope.org/zope', u'configure')>

A directive component has some interesting atrributes, such as the name,

>>> root.name
(u'http://namespaces.zope.org/zope', u'configure')

the schema that describes the directive,

>>> root.schema
<InterfaceClass zope.configuration.zopeconfigure.IZopeConfigure>

the attributes of the XML element,

>>> dict(root.attrs)
{}

the configuration context for the directive, which can be used to resolve objects and/or generate absolute paths of files,

>>> root.context
<zope.configuration.config.ConfigurationMachine object at ...>

the parser info object,

>>> info = repr(root.info)
>>> info = info.replace('\\', '/')   # Windows fix
>>> print(info)
File ".../zope/app/apidoc/codemodule/configure.zcml", ...

the sub-directives,

>>> root.subs[:2]
[<Directive (u'http://namespaces.zope.org/zope', u'class')>,
 <Directive (u'http://namespaces.zope.org/zope', u'class')>]

and finally a list of all prefixes.

>>> pprint(root.prefixes)
{u'http://namespaces.zope.org/apidoc': u'apidoc',
 u'http://namespaces.zope.org/browser': u'browser',
 u'http://namespaces.zope.org/zope': None}