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

  >>> 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']))
  {'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'}