=========================
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 `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

  >>> cm.keys()
  []


Module
------

The `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 ``get(key, default=None)`` 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 = module['tests'].keys()
  >>> names.sort()
  >>> names
  ['Root', 'rootLocation', 'setUp', 'tearDown', 'test_suite']


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'

  >>> klass.getDocString()[:41]
  'Represent the complete API Documentation.'

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
`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] #doctest:+NORMALIZE_WHITESPACE
  ('get', <unbound method APIDocumentation.get>,
   <InterfaceClass zope.interface.common.mapping.IReadMapping>)


Let's have a closer look at the `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')

  >>> class Blah(object):
  ...      zope.interface.implements(IBlah)
  ...      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)

  >>> 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

  >>> class Blah(object):
  ...      zope.interface.implements(IBlah)
  ...
  ...      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 documenation:

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


Function
--------

Function are pretty much documented in the same way as all other code
documentation objects and provides a similar API to the classes. A function
documenation 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.txt')
  >>> readme = codemodule.text.TextFile(path, 'README.txt', module)

we can ask it for the content of the file:

  >>> print readme.getContent()[26:51]
  Code Documentation Module


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 #doctest:+ELLIPSIS
  <zope.configuration.config.ConfigurationMachine object at ...>

the parser info object,

  >>> info = `root.info`

  # Windows fix
  >>> info = info.replace('\\', '/')

  >>> print info #doctest:+ELLIPSIS
  File
  ".../zope/app/apidoc/codemodule/configure.zcml",
  line 1.0-53.0

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}

