#
# objdoc: epydoc object documentation classes
# Edward Loper
#
# Created [01/30/01 05:18 PM]
# $Id: objdoc.py,v 1.85 2004/03/19 19:02:42 edloper Exp $
#

# To do:
#    - overriding/doc inheritance
#    - inheritance source pseudo-ClassDocs?
#    - clean up
#    - fix package link (when there's 1 package)

"""
Support for C{ObjDoc}s, which encode the information about a Python
object that is necessary to create its documentation.  C{ObjDoc}s are
created and managed by the C{DocMap} class, which acts like a
dictionary from L{UID}s to C{ObjDoc}s.

Textual documentation entries (e.g., module descriptions, method
descriptions, variable types, see-also entries) are encoded as
L{ParsedDocstrings<markup.ParsedDocstring>}.

Each Python object is identified by a globally unique identifier,
implemented with the L{UID} class.  These identifiers are also used by
the C{Link} class to implement crossreferencing between C{ObjDoc}s.

@var DEFAULT_DOCFORMAT: The default value for C{__docformat__}, if it
is not specified by modules.  C{__docformat__} is a module variable
that specifies the markup language for the docstrings in a module.
Its value is a string, consisting the name of a markup language,
optionally followed by a language code (such as C{en} for English).
Some typical values for C{__docformat__} are:

    >>> __docformat__ = 'plaintext'
    >>> __docformat__ = 'epytext'
    >>> __docformat__ = 'epytext en'
"""
__docformat__ = 'epytext en'

##################################################
## Imports
##################################################

import inspect, UserDict, string, new, re, sys, types
import parser, symbol, token

import epydoc.markup as markup
from epydoc.uid import UID, Link, make_uid, findUID

# Python 2.2 types
from epydoc.uid import _WrapperDescriptorType
from epydoc.uid import _MethodDescriptorType
from epydoc.uid import _PropertyType

# Zope extension class type
from epydoc.uid import _ZopeType, _ZopeMethodType, _ZopeCMethodType

# This is used when we get a bad field tag, to distinguish between
# unknown field tags, and field tags that were just used in a bad
# context. 
_KNOWN_FIELD_TAGS = ('var', 'variable', 'ivar', 'ivariable',
                     'cvar', 'cvariable', 'type', 'group', 'sort',
                     'return', 'returns', 'rtype', 'returntype',
                     'param', 'parameter', 'arg', 'argument',
                     'raise', 'raises', 'exception', 'except',
                     'summary', 'keyword', 'kwarg', 'kwparam',
                     'undocumented', 'include')

##################################################
## __docformat__
##################################################
# Move this to epydoc.markup?

KNOWN_DOCFORMATS = ('plaintext', 'epytext', 'restructuredtext', 'javadoc')
DEFAULT_DOCFORMAT = 'epytext'
def set_default_docformat(new_format):
    """
    Change the default value for C{__docformat__} to the given value.
    The current default value for C{__docformat__} is recorded in
    C{DEFAULT_DOCFORMAT}.

    @param new_format: The new default value for C{__docformat__}
    @type new_format: C{string}
    @see: L{DEFAULT_DOCFORMAT}
    @rtype: C{None}
    """
    global DEFAULT_DOCFORMAT
    DEFAULT_DOCFORMAT = new_format

##################################################
## ObjDoc
##################################################

#////////////////////////////////////////////////////////
#// Var, Param, and Raise
#////////////////////////////////////////////////////////

class Var:
    """
    The documentation for a variable.  This documentation consists of
    the following fields:
    
        - X{uid}: The variable's UID
        - X{descr}: A description of the variable
        - X{type}: A description of the variable's type
        - X{has_value}: A flag indicating whether a variable has a
          value.  If this flag is true, then the value can be accessed
          via the C{Var}'s UID.
        
    C{Var}s are used by L{ModuleDoc} to document variables; and by
    L{ClassDoc} to document instance and class variables.
    """
    def __init__(self, name, uid, descr=None, type=None, has_value=1,
                 autogenerated=0):
        """
        Construct the documentation for a variable.

        @param uid: A unique identifier for the variable.
        @type uid: C{string}
        @param descr: A description of the variable.
        @type descr: L{markup.ParsedDocstring}
        @param type: A description of the variable's type.
        @type type: L{markup.ParsedDocstring}
        @param has_value: Whether the documented variable has a value.
        @type has_value: C{boolean}
        @param autogenerated: Whether the variable was automatically
            detected (and not explicitly documented).
        @type autogenerated: C{boolean}
        """
        self._name = name
        self._uid = uid
        self._descr = descr
        self._type = type
        self._has_value = has_value
        self._autogen = autogenerated
        
    def uid(self):
        """
        @return: The UID of the variable documented by this C{Var}.
        @rtype: L{UID}
        """
        return self._uid

    def name(self):
        """
        @return: The short name of this variable.
        @rtype: C{string}
        """
        return self._name
    
    def type(self):
        """
        @return: A description of this variable's type
        @rtype: L{markup.ParsedDocstring}
        """
        return self._type
    
    def descr(self):
        """
        @return: A description of this variable.
        @rtype: L{markup.ParsedDocstring}
        """
        return self._descr

    def has_value(self):
        """
        @return: True if the variable documented by this C{Var} has a
            value.  If this function returns true, then the value can
            be accessed via the C{Var}'s UID.
        @rtype: C{boolean}
        """
        return self._has_value

    def autogenerated(self):
        """
        @return: True if this variable documentation was generated
            automatically from a module variable (i.e., not from a
            C{@var} field or a C{@type} field).
        @rtype: C{boolean}
        """
        return self._autogen

    def is_private(self):
        """
        @return: True if this variable is a private object.  This is
            equivalant to C{self.uid().is_private()}
        """
        return self._uid.is_private()
    
    def is_public(self):
        """
        @return: True if this variable is a public object.  This is
            equivalant to C{self.uid().is_public()}
        """
        return self._uid.is_public()
    
    def __repr__(self):
        return '<Variable %s>' % self._uid

    # In 3.0, Var will be replaced with an ObjDoc class, and Vars will
    # be linked to with Links.  In the mean time, this lets Vars be
    # treated like Links in some circumstances.
    target = uid

class Param:
    """
    The documentation for a function parameter.  This documentation
    consists of the following fields:

        - X{name}: The name of the parameter
        - X{descr}: A description of the parameter
        - X{type}: A description of the parameter's type
        - X{default}: The parameter's default value

    C{Param}s are used by L{FuncDoc} to document parameters.

    @group Shared Parameter Lists: shared_descr_params, listed_under,
        set_shared_descr_params, remove_shared_descr_param
    """
    def __init__(self, name, descr=None, type=None, default=None):
        """
        Construct the documentation for a parameter.

        @param name: The name of the parameter.
        @type name: C{string}
        @param descr: A description of this parameter.
        @type descr: L{markup.ParsedDocstring}
        @param type: A description of this parameter's type.
        @type type: L{markup.ParsedDocstring}
        @param default: A string representation of the parameter's
            default value; or C{None} if it has no default value.
        @type default: C{string} or C{None}
        """
        self._name = name
        self._descr = descr
        self._type = type
        self._default = default
        self._shared_descr_params = None
        self._listed_under = None
        
    def name(self):
        """
        @return: The name of this parameter.
        @rtype: C{string}
        """
        return self._name
    
    def type(self):
        """
        @return: A description of this parameter's type.
        @rtype: L{markup.ParsedDocstring}
        """
        return self._type
    
    def descr(self):
        """
        @return: A description of this parameter.
        @rtype: L{markup.ParsedDocstring}
        """
        return self._descr
    
    def default(self):
        """
        @return:  A string representation of the parameter's
            default value; or C{None} if it has no default value.
        @rtype: C{string} or C{None}
        """
        return self._default

    def shared_descr_params(self):
        """
        @rtype: C{None} or C{list} of L{Param}
        @return: A list of C{Param}s that share the description given
        by this parameter.  This is used in cases where multiple
        parameters are descrubed in one C{@param} field, such as::

            @param *varargs, **kwargs: extra args for f.

        In this case, the description will be added to C{varargs}'s
        C{Param}; and C{vararg}'s C{Param} will list C{kwargs} in its
        C{shared_descr_params()} list.

        For parameters described by normal (single-parameter)
        C{@param} fields, C{shared_descr_params} will return C{None}.
        """
        return self._shared_descr_params

    def listed_under(self):
        """
        @rtype: C{None} or L{Param}
        @return: The parameter that this C{Param} is listed under,
            if any.  This parameter is listed under parameter C{M{p}}
            if it contained in C{M{p}.shared_descr_params()}.
        @see: L{shared_descr_params}
        """
        return self._listed_under

    def set_shared_descr_params(self, shared_descr_params):
        """
        Register a list of C{Param}s that share the description given
        by this parameter.
        @type shared_descr_params: C{list} of L{Param}
        @param shared_descr_params: The C{Param}s that share the
            description given by this parameter
        @rtype: C{None}
        @see: L{shared_descr_params}
        """
        if self._shared_descr_params is not None:
            for p in self._shared_descr_params: p._listed_under = None
        self._shared_descr_params = shared_descr_params
        if self._shared_descr_params is not None:
            for p in self._shared_descr_params: p._listed_under = self

    def remove_shared_descr_param(self, param):
        """
        Remove a parameter from the list of C{Param}s that share the
        description given by this parameter.
        @type param: L{Param}
        @see: L{shared_descr_params}
        """
        self._shared_descr_params.remove(param)
        param._listed_under = None
    
    def set_type(self, type):
        """
        Set this parameter's type.
        
        @param type: The new description of this parmeter's type.
        @type type: L{markup.ParsedDocstring}
        @rtype: C{None}
        """
        self._type = type
        
    def set_descr(self, descr):
        """
        Set this parameter's description.
        
        @param descr: The new description of this parameter.
        @type descr: L{markup.ParsedDocstring}
        @rtype: C{None}
        """
        self._descr = descr
        
    def set_default(self, default):
        """
        Set this parameter's default value.
        
        @param default: A string representation of the parameter's
            default value; or C{None} if it has no default value.
        @type default: C{string} or C{None}
        @rtype: C{None}
        """
        self._default = default
        
    def __repr__(self):
        return '<Parameter '+self._name+'>'

class Raise:
    """
    The documentation for the raising of an exception.  This consists
    of the exception's name and its description.  Exceptions are used
    by L{FuncDoc}.
    """
    def __init__(self, name, descr):
        """
        Construct the documentation for the raising of an exception.

        @param name: The name of the exception.
        @type name: C{string}
        @param descr: A description of the circumstances under which
            this exception is raised.
        @type descr: L{markup.ParsedDocstring}
        """
        self._name = name
        self._descr = descr
        
    def name(self):
        """
        @return: The name of the exception.
        @rtype: C{string}
        """
        return self._name
    
    def descr(self):
        """
        @return: A description of the circumstances under which this
            exception is raised.
        @rtype: L{markup.ParsedDocstring}
        """
        return self._descr
        
    def __repr__(self):
        return '<Raise '+self._name+'>'

#////////////////////////////////////////////////////////
#// Generalized Fields
#////////////////////////////////////////////////////////

class DocField:
    """
    A generic docstring field.  Docstring field are used to describe
    specific information about an object, such as its author or its
    version.  Generic docstring fields are fields that take no
    arguments, and are displayed as simple sections.

    @ivar tags: The set of tags that can be used to identify this
        field.
    @ivar singular: The label that should be used to identify this
        field in the output, if the field contains one value.
    @ivar plural: The label that should be used to identify this
        field in the output, if the field contains multiple values.
    @ivar short: If true, then multiple values should be combined
        into a single comma-delimited list.  If false, then
        multiple values should be listed separately in a bulleted
        list.
    @ivar multivalue: If true, then multiple values may be given
        for this field; if false, then this field can only take a
        single value, and a warning should be issued if it is
        redefined.
    """
    def __init__(self, tags, label, plural=None,
                 short=0, multivalue=1):
        if type(tags) in (types.TupleType, types.ListType):
            self.tags = tuple(tags)
        elif type(tags) == types.StringType:
            self.tags = (tags,)
        else: raise TypeError('Bad tags: %s' % tags)
        self.singular = label
        if plural is None: self.plural = label
        else: self.plural = plural
        self.multivalue = multivalue
        self.short = short

    def __cmp__(self, other):
        if not isinstance(other, DocField): return -1
        return cmp(self.tags, other.tags)
    
    def __hash__(self):
        return hash(self.tags)

    def __repr__(self):
        return '<Field: %s>' % self.tags[0]

#////////////////////////////////////////////////////////
#// Base ObjDoc Class
#////////////////////////////////////////////////////////
class ObjDoc:
    """
    A base class for encoding the information about a Python object
    that is necessary to create its documentation.  This base class
    defines the following pieces of documentation:

        - X{uid}: The object's unique identifier.
        - X{descr}: A full description of the object.
        - X{summary}: A summary description of the object.
        - X{fields}: A list of generic docstring fields, such
          as authors, version, and notes.  The list of standard
          generic docstring fields is defined by the variable
          L{ObjDoc.STANDARD_FIELDS}; but the user may also add new
          generic docstring fields (via C{@newfield} or
          C{__extra_epydoc_fields__}).
        - X{sortorder}: The object's sort order, as defined by the
          C{@sort} field.
        - X{groups}: Groupings for contained objects (or
          parameters).

    @group Accessors: documented, uid, descr, fields, field_values,
        sortorder, groups, by_group, has_docstring, summary,
        defines_groups
    @group Error Handling: _print_errors, _parse_warnings,
        _parse_errors, _field_warnings, _misc_warnings
    @group Docstring Parsing: __parse_docstring, _process_field,
        _descr, _fields, STANDARD_FIELDS

    @ivar _uid: The object's unique identifier
    @type _uid: L{UID}
    @ivar _descr: The object's description, encoded as epytext.
    @type _descr: L{markup.ParsedDocstring}

    @ivar _fields: Documentation fields that were extracted from
        the object's docstring.  The list of fields that are
        accepted by epydoc is defined by L{STANDARD_FIELDS}.
    @type _fields: C{dictionary} from L{DocField} to C{list} of
                   L{markup.ParsedDocstring}
    @cvar STANDARD_FIELDS: The list of standard docstring fields that
        epydoc accepts.  The order of fields is significant: when the
        fields are rendered, they will be listed in the order that
        they are given in this list.  Note that this list does X{not}
        include special fields, such as \"group\"; it just includes
        \"simple\" fields, which contain a single textual description
        or a list of textual descriptions.
    @type STANDARD_FIELDS: C{List} of L{DocField}

    @ivar _misc_warnings: Warnings that are not related to the
        object's docstring.
    @ivar _parse_warnings: Warnings generated when parsing the
        object's docstring.
    @ivar _parse_errors: Errors generated when parsing the object's
        docstring.
    @ivar _field_warnings: Warnings generated when processing the
        object's docstring's fields.
    """
    # Note: order is significant.
    STANDARD_FIELDS = [
        # If it's depreciated, put that first.
        DocField(['depreciated'], 'Depreciated', multivalue=0),

        # Status info
        DocField(['version'], 'Version', multivalue=0),
        DocField(['date'], 'Date', multivalue=0),
        DocField(['status'], 'Status', multivalue=0),
        
        # Bibliographic Info
        DocField(['author', 'authors'], 'Author', 'Authors', short=1),
        DocField(['contact'], 'Contact', 'Contacts', short=1),
        DocField(['organization', 'org'], 'Organization', 'Organizations'),
        DocField(['copyright', '(c)'], 'Copyright', multivalue=0),
        DocField(['license'], 'License', multivalue=0),

        # Various warnings etc.
        DocField(['bug'], 'Bug', 'Bugs'),
        DocField(['warning', 'warn'], 'Warning', 'Warnings'),
        DocField(['attention'], 'Attention'),
        DocField(['note'], 'Note', 'Notes'),

        # Formal conditions
        DocField(['requires', 'require', 'requirement'], 'Requires'),
        DocField(['precondition', 'precond'],
                 'Precondition', 'Preconditions'),
        DocField(['postcondition', 'postcond'],
                 'Postcondition', 'Postconditions'),
        DocField(['invariant'], 'Invariant'),

        # When was it introduced (version # or date)
        DocField(['since'], 'Since', multivalue=0),

        # Crossreferences
        DocField(['see', 'seealso'], 'See Also', short=1),

        # Future Work
        DocField(['todo'], 'To Do'),
        ]
    def __init__(self, uid, verbosity=0, docstring=None):
        """
        Create the documentation for the given object.
        
        @param uid: The UID of the object to document.
        @type uid: L{UID}
        @param verbosity: The verbosity of output produced when
            creating documentation for the object.  More positive
            numbers produce more verbose output; negative numbers
            supress warnings and errors.
        @type verbosity: C{int}
        """
        obj = uid.value()
        self._uid = uid

        # Default: no description
        self._descr = None
        self._summary = None

        # Default: no sort order
        self._sortorder = None
        self._sortorder_contains_regexp = 0

        # Groups:
        self._groupnames = [None]
        self._name2groupnum = {}
        self._group2groupnum = {None: 0} # used to merge groups.
        self._regexp_groups = [] # (regexp, groupnum) pairs

        # Undocumented fields.  These are declared with @undocumented,
        # and filterd out by ObjDoc._sort().
        self._undocumented = []

        # Initialize errors/warnings, and remember verbosity.
        self.__verbosity = verbosity
        self._parse_errors = []
        self._parse_warnings = []
        self._field_warnings = []
        self._misc_warnings = []

        # Look up our module.  We use this to look up both
        # __docformat__ and __extra_epydoc_fields__.
        if self._uid.is_module(): module = self._uid
        else: module = self._uid.module()

        # Give a warning if there's an __epydoc_sort__ attribute.
        if hasattr(obj, '__epydoc_sort__'):
            estr = 'Warning: __epydoc_sort__ is depreciated'
            self._misc_warnings.append(estr)
        
        # Initialize fields.  Add any extra fields.
        self._fieldtypes = self.STANDARD_FIELDS[:]
        if module is not None:
            # Gather any extra fields from this module and all of its 
            # ancestor packages. 
            extra_fields = [] 
            m = module 
            while m is not None: 
                try: extra_fields += m.value().__extra_epydoc_fields__ 
                except: pass 
                m = m.parent() 
            
            # Create the specified extra fields.
            for field in extra_fields:
                try:
                    if type(field) == types.StringType:
                        self._fieldtypes.append(DocField(field.lower(), field))
                    else:
                        self._fieldtypes.append(DocField(*field))
                except:
                    estr = 'Bad extra epydoc field: %r' % field
                    misc_warnings.append(estr)

        # Initialize the fields map.  This is where the actualy field
        # bodies are stored.
        self._fields_map = {}

        # Look up __docformat__
        self._docformat = DEFAULT_DOCFORMAT
        if module is not None:
            try: self._docformat = module.value().__docformat__.lower()
            except: pass
            
        # Ignore __docformat__ region codes.
        try: self._docformat = self._docformat.split()[0]
        except: pass

        # Check that it's an acceptable format.  Only issue a warning
        # for the module, not a separate warning for every object in
        # the module.
        if (self._docformat not in KNOWN_DOCFORMATS and
            self._uid.is_module()):
            estr = ("Unknown __docformat__ value %s; " % self._docformat +
                    "treating as plaintext.")
            self._misc_warnings.append(estr)
            self._docformat = 'plaintext'

        # If there's a doc string, parse it.
        if docstring is None: docstring = _getdoc(obj)
        if type(docstring) == types.StringType: docstring = docstring.strip()
        if docstring:
            self._has_docstring = 1
            self.__parse_docstring(docstring)
        else:
            self._has_docstring = 0
    
        # Sort the fields in the order specified by _fieldtypes.
        self._fields = [f for f in self._fieldtypes
                        if self._fields_map.has_key(f)]

    #////////////////////////////
    #// Accessors
    #////////////////////////////

    def has_docstring(self):
        """
        @return: True if the object documented by this C{ObjDoc} has a
        docstring.
        @rtype: C{boolean}
        """
        return self._has_docstring
            
    def uid(self):
        """
        @return: The UID of the object documented by this C{ObjDoc}.
        @rtype: L{UID}
        """
        return self._uid

    def descr(self):
        """
        @return: A description of the object documented by this
        C{ObjDoc}.
        @rtype: L{markup.ParsedDocstring}
        """
        return self._descr

    def summary(self):
        """
        @return: A summary of the description of the object documented
            by this C{ObjDoc}.
        @rtype: L{markup.ParsedDocstring}
        """
        if self._descr is None: return None
        if self._summary is None: self._summary = self._descr.summary()
        return self._summary

    def fields(self):
        """
        @return: A list of the fields that are given values in the
            docstring of the object documented by this C{ObjDoc}.
            The fields are listed in the order that they first
            appear in the docstring.
        @rtype: C{list} of L{DocField}
        """
        return self._fields

    def field_values(self, field):
        """
        @return: A list of the values that are specified for the
            given field in the docstring of the object documented
            by this C{ObjDoc}.  Values are listed in the order that
            they appear in the docstring.
        @rtype: C{list} of L{markup.ParsedDocstring}
        """
        return self._fields_map[field]
    
    def sortorder(self):
        """
        @return: A list specifying the sort order that should be used
            for the object's children.  Elements that are in this list
            should appear before elements that are not in this list;
            and elements that are in this list should appear in the
            order that they appear in this list.
        @rtype: C{list} of C{string}
        """
        return self._sortorder

    def defines_groups(self):
        """
        @return: True if the object documented by this C{ObjDoc}
            defines any groups.
        @rtype: C{boolean}
        """
        return len(self._groupnames) != 1

    def groups(self):
        """
        @return: A list of the names of the groups that are defined
            for the object documented by this C{ObjDoc}.  To divide
            a set of children into groups, use L{by_group}.
        @rtype: C{list} of C{string}
        """
        return self._groupnames[1:]

    def by_group(self, elts):
        """
        Divide a set of elements into groups.  Typical usage:

            >>> grouped_funcs = fdoc.by_group(fdoc.functions())
            >>> for (group, members) in grouped_funcs:
            ...     print group, members

        @param elts: The set of elements that should be divided into
            groups.  These elements should be C{Link}s, C{Var}s, or
            C{Param}s specifying child objects of the object documented
            by this C{ObjDoc}.
        @type elts: C{list} of (L{Link} or L{Var} or L{ObjDoc})
        @return: A list of tuples of the form C{(name, members)},
            where C{name} is the name of a group; and C{members} is a
            list of the elements in C{elts} that are in the group.
            Group membership is tested with the C{elt.name()} method,
            so every element in C{elts} must define that method.
        @rtype: C{list} of C{(string, elt)}
        """
        # Check for the special case of no groups: (for speed)
        if len(self._groupnames) == 1: return [(None, elts)]

        # Divide into groups.  Do this in O(len(elts)+len(groups)),
        # instead of the more intuitive algorithm which gives
        # O(len(elts)*len(groups)).
        name2groupnum = self._name2groupnum
        grouped = [(g, []) for g in self._groupnames]
        for elt in elts:
            groupnum = name2groupnum.get(elt.name(), 0)
            grouped[groupnum][1].append(elt)

        # If there are any regexp groups, then handle them.  This
        # isn't nearly as optimized as normal group access, but
        # presumably it will be significantly less common (esp if
        # inheritance=grouped).
        if self._regexp_groups:
            ungrouped = grouped[0][1]
            for (regexp, groupnum) in self._regexp_groups:
                for i in range(len(ungrouped)-1, -1, -1):
                    elt = ungrouped[i]
                    if regexp.match(elt.name()):
                        grouped[groupnum][1].append(elt)
                        del ungrouped[i]

        # Is it worth-while to eliminate empty groups here?  Or should
        # we just leave them in?
        grouped = [pair for pair in grouped if len(pair[1]) != 0]

        return grouped

    #////////////////////////////
    #// Protected
    #////////////////////////////

    def _sort(self, items):
        """
        @return: A copy of the list C{items}, sorted first by
            C{sortorder}, and then by name.  Item names are
            sorted as follows:
              - C{'__init__'} < anything else
              - public < private
              - otherwise, alphabetically (case isensitive).
        @type items: C{list} of (L{UID} or L{Link} or L{Var})
        """
        if not items: return items
        sortorder = self._sortorder

        # Filter out anything that is declared as undocumented.
        if self._undocumented:
            for i in range(len(items)-1, -1, -1):
                for regexp in self._undocumented:
                    if regexp.match(items[i].name()):
                        del items[i]
                        break

        # If an explicit sort order was given (via @sort), then
        # use it to sort the objects.
        so_items = []
        if sortorder is not None:
            # Handle regexps in the sort order.  This is done somewhat
            # inefficently, but it shouldn't be used that much. :)
            if self._sortorder_contains_regexp:
                sortorder = []
                for name in self._sortorder:
                    if type(name) is types.StringType:
                        sortorder.append(name)
                    else:
                        # If it's a regexp, then find everything that
                        # it matches.
                        for item in items:
                            if name.match(item.name()):
                                sortorder.append(item.name())

            item_by_name = {}
            for item in items:
                item_by_name[item.name()] = item
            for name in sortorder:
                item = item_by_name.get(name)
                if item is not None:
                    so_items.append(item)
                    del item_by_name[name]
            items = item_by_name.values()
            
        # Use the decorate-sort-undecorate pattern to make sorting
        # more efficient.
        decorated = [(item.name()!='__init__', item.is_private(),
                      item.name().lower(), item) for item in items]
        decorated.sort()
        return so_items + [d[-1] for d in decorated]

    def _process_field(self, tag, arg, descr, warnings):
        """
        Process a field from this object's docstring.
        C{ObjDoc._process_field} be overridden by child classes of
        C{ObjDoc}, and called as the default case.
        @type tag: C{string}
        @param tag: The field's tag name
        @type arg: C{string}
        @param arg: The field's optional argument (or C{None} if no
            argument was specified).
        @type descr: L{markup.ParsedDocstring}
        @param descr: The field's body.
        @param warnings: A list of warnings that have been encountered.
            If any new warnings are encountered, then they should be
            appended to this list.
        """
        if tag == 'group':
            if arg is None:
                warnings.append(tag+' expected an argument (group name)')
                return
            try: idents = _descr_to_identifiers(descr)
            except ValueError, e:
                warnings.append('Bad group identifier list')
                return
            if not self._group2groupnum.has_key(arg):
                self._groupnames.append(arg)
                self._group2groupnum[arg] = len(self._groupnames)-1
            for ident in idents:
                if '*' in ident:
                    # It's a regexp-type group.
                    regexp = '^%s$' % ident.replace('*', '(.*)')
                    self._regexp_groups.append((re.compile(regexp),
                                                self._group2groupnum[arg]))
                else:
                    self._name2groupnum[ident] = self._group2groupnum[arg]
            # @group implies @sort:
            self._process_field('sort', None, descr, warnings)
            return
        
        if tag in ('deffield', 'newfield'):
            try:
                field = _descr_to_docfield(arg, descr)
                self._fieldtypes.append(field)
            except ValueError, e:
                warnings.append('Bad %s: %s' % (tag, e))
            return
        
        if tag == 'sort':
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            if self._sortorder is None: self._sortorder = []
            try: idents = _descr_to_identifiers(descr)
            except ValueError, e:
                warnings.append('Bad sort order list')
                return
            for ident in idents:
                if '*' in ident:
                    regexp = '^%s$' % ident.replace('*', '(.*)')
                    self._sortorder.append(re.compile(regexp))
                    self._sortorder_contains_regexp = 1
                else:
                    self._sortorder.append(ident)
            return

        if tag == 'summary':
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            if self._summary is not None:
                warnings.append(tag+' redefined.')
                return
            self._summary = descr
            return

        if tag == 'undocumented':
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            try: idents = _descr_to_identifiers(descr)
            except ValueError, e:
                warnings.append('Bad @undocumented list')
                return
            for ident in idents:
                regexp = '^%s$' % ident.replace('*', '(.*)')
                self._undocumented.append(re.compile(regexp))
            return

        if tag == 'include':
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            try: (source,) = _descr_to_identifiers(descr)
            except ValueError:
                warnings.append('Bad @include source')
                return
            source_uid = findUID(source, self._uid)
            if source_uid is None:
                warnings.append('Unable to resolve @include: %r' % source)
                return
            # Extract the docstring.
            docstring = _getdoc(source_uid.value())
            if type(docstring) != types.StringType: return
            docstring = docstring.strip()
            if not docstring: return
            self.__include(docstring)
                
        for field in self._fieldtypes:
            if tag not in field.tags: continue
            
            # Special handling for @todo <version>: ...
            if tag == 'todo' and arg is not None:
                field = DocField(['todo:%s' % arg],
                                 'To Do for Version %s' % arg)
                if not self._fields_map.has_key(field):
                    self._fieldtypes.append(field)
                arg = None
                    
            # Create the field, if it doesn't exist yet.
            if not self._fields_map.has_key(field):
                self._fields_map[field] = []
            values = self._fields_map[field]

            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            if not field.multivalue and len(values) > 0:
                warnings.append(tag+ ' redefined')
                del values[:]
            values.append(descr)
            return

        if tag in _KNOWN_FIELD_TAGS:
            warnings.append('Invalid context for field tag %r' % tag)
        else:
            warnings.append('Unknown field tag %r' % tag)
        
    def _print_errors(self, stream=None):
        """
        Print any errors that were encountered while constructing this
        C{ObjDoc} to C{stream}.  This method should be called at the
        end of the constructor of every class that is derived from
        C{ObjDoc} (I{after} the base C{ObjDoc} constructor has been
        called).
        
        @rtype: C{None}
        """
        parse_warnings = self._parse_warnings
        parse_errors = self._parse_errors
        field_warnings = self._field_warnings
        misc_warnings = self._misc_warnings

        # Set it every time, in case something changed sys.stderr
        # (like epydoc.gui :) )
        if stream is None: stream = sys.stderr
        
        # Supress warnings/errors, if requested
        if self.__verbosity <= -1: misc_warnings = []
        if self.__verbosity <= -1: parse_warnings = []
        if self.__verbosity <= -2: field_warnings = []
        if self.__verbosity <= -3: parse_errors = []
        
        # Print the errors and warnings.
        if (parse_warnings or parse_errors or
            field_warnings or misc_warnings):
            
            # Figure out our file and line number, if possible.
            try:
                (filename, startline) = _find_docstring(self._uid)
                if startline is None: startline = 0
            except:
                (filename, startline) = (None, 0)

            # Print the location of the error/warning.
            if not (parse_warnings or parse_errors or field_warnings):
                where = 'In %s' % self._uid
            else:
                where = 'In %s docstring' % self._uid
            if stream.softspace: print >>stream
            print >>stream, '='*75
            if filename is not None:
                print >>stream, filename
                print >>stream, ('%s (line %s):' % (where, startline+1))
            else:
                print >>stream, '%s:' % where
            print >>stream, '-'*75

            # Print parse errors.
            for error in parse_errors:
                if type(error) == types.StringType:
                    if startline is None:
                        print >>stream, '       '+error
                    else:
                        estr =' Error: %s' % error
                        estr = markup.wordwrap(estr, 7, startindex=7).strip()
                        print >>stream, '     - %s' % estr
                else:
                    error.set_linenum_offset(startline+1)
                    print >>stream, error

            # Print parse warnings.
            for warning in parse_warnings:
                warning.set_linenum_offset(startline+1)
                print >>stream, warning

            # Print field warnings
            for warning in field_warnings+misc_warnings:
                if startline is None:
                    print >>stream, '       '+warning
                else:
                    estr =' Warning: %s' % warning
                    estr = markup.wordwrap(estr, 7, startindex=7).strip()
                    print >>stream, '     - %s' % estr
                    
            print >>stream
        
    #////////////////////////////
    #// Private
    #////////////////////////////

    def __parse_docstring(self, docstring):
        errors = []
        parsed_docstring = markup.parse(docstring, self._docformat, errors)
        descr, fields = parsed_docstring.split_fields(errors)
        self._descr = descr

        # Divide errors into fatal & non-fatal.
        self._parse_errors = [e for e in errors if e.is_fatal()]
        self._parse_warnings = [e for e in errors if not e.is_fatal()]
        
        for field in fields:
            self._process_field(field.tag(), field.arg(),
                                field.body(), self._field_warnings)

    def __include(self, docstring):
        errors = []
        parsed_docstring = markup.parse(docstring, self._docformat, errors)
        descr, fields = parsed_docstring.split_fields(errors)
        self._descr += descr

        # Mark errors as coming from included docstring.
        for e in errors: e._descr += ' (FROM INCLUDED DOCSTRING)'

        # Divide errors into fatal & non-fatal.
        self._parse_errors += [e for e in errors if e.is_fatal()]
        self._parse_warnings += [e for e in errors if not e.is_fatal()]
        
        for field in fields:
            self._process_field(field.tag(), field.arg(),
                                field.body(), self._field_warnings)

        
        
        
#////////////////////////////////////////////////////////
#// ModuleDoc
#////////////////////////////////////////////////////////
class ModuleDoc(ObjDoc):
    """
    The documentation for a module or package.  This documentation
    consists of standard pieces of documentation (as defined in
    L{ObjDoc}), and the following module-specific pieces of
    documentation:
    
        - X{classes}: A list of all classes contained in the
          module/package.
        - X{functions}: A list of all functions contained in the
          module/package.
        - X{variables}: A list of all variables contained in the
          module/package.
        - X{modules}: A list of all modules contained in the
          package (packages only).
        - X{imported objects}: Lists of imported objects, sorted
          by type.

    @group Accessors: functions, classes, variables,
        imported_functions, imported_classes, package, ispackage,
        ismodule, modules, imported_variables, imported_modules
    @group Modifiers: remove_autogenerated_variables, add_modules
    @group Import Detection: _find_defined_vars,
        _find_imported_variables

    @type _classes: C{list} of L{Link}
    @ivar _classes: A list of all classes contained in the
        module/package.     
    @type _functions: C{list} of L{Link}
    @ivar _functions: A list of all functions contained in the
        module/package.
    @type _variables: C{list} of L{Var}
    @ivar _variables: A list of all variables defined by this
        module/package. 
    @type _modules: C{list} of L{Link}
    @ivar _modules: A list of all modules conained in the package
        (package only).
    """
    def __init__(self, uid, verbosity=0):
        mod = uid.value()

        # Variables:
        self._tmp_var = {}
        self._tmp_type = {}

        ObjDoc.__init__(self, uid, verbosity)

        # If mod is a package, then it will contain a __path__
        if mod.__dict__.has_key('__path__'): self._modules = []
        else: self._modules = None

        # Handle functions, classes, and variables.
        self._classes = []
        self._functions = []
        self._variables = []
        self._imported_classes = []
        self._imported_functions = []
        self._imported_variables = []
        self._imported_modules = []
        for (field, val) in mod.__dict__.items():
            vuid = make_uid(val, self._uid, field)
                
            # Don't do anything for these special variables:
            if field in ('__builtins__', '__doc__', '__all__', '__file__',
                         '__path__', '__name__', 
                         '__extra_epydoc_fields__', '__docformat__'):
                continue
            # Don't do anything if we can't get a UID.
            if vuid is None: continue
            
            # Record imported modules.
            if vuid.is_module():
                self._imported_modules.append(Link(field, vuid))

            # Is it a function?
            elif vuid.is_function() or vuid.is_builtin_function():
                if vuid.module() == self._uid:
                    self._functions.append(Link(field, vuid))
                else:
                    self._imported_functions.append(Link(field, vuid))

            # Is it a class?
            elif vuid.is_class():
                if vuid.module() == self._uid:
                    self._classes.append(Link(field, vuid))
                else:
                    self._imported_classes.append(Link(field, vuid))

            # Is it a variable?
            else:
                autogen = 1 # is it autogenerated?
                descr = self._tmp_var.get(field)
                if descr is not None:
                    del self._tmp_var[field]
                    autogen = 0
                typ = self._tmp_type.get(field)
                if typ is not None:
                    del self._tmp_type[field]
                    autogen = 0
                else: typ = markup.parse_type_of(val)
                self._variables.append(Var(field, vuid, descr,
                                           typ, 1, autogen))

        # Add the remaining variables
        for (name, descr) in self._tmp_var.items():
            typ = self._tmp_type.get(name)
            if typ is not None: del self._tmp_type[name]
            vuid = make_uid(None, self._uid, name)
            self._variables.append(Var(name, vuid, descr, typ, 0))

        # Attempt to split variables into imported and non-imported.
        # (Only possible if we can parse the module's .py file)
        self._find_imported_variables()

        # Make sure we used all the type fields.
        if self._tmp_type != {}:
            for key in self._tmp_type.keys():
                estr = '@type for unknown variable %s' % key
                self._field_warnings.append(estr)
        del self._tmp_var
        del self._tmp_type

        # Put everything in sorted order.
        self._modules = self._sort(self._modules)
        self._classes = self._sort(self._classes)
        self._functions = self._sort(self._functions)
        self._variables = self._sort(self._variables)
        self._imported_classes = self._sort(self._imported_classes)
        self._imported_functions = self._sort(self._imported_functions)
        self._imported_variables = self._sort(self._imported_variables)
        self._imported_modules = self._sort(self._imported_modules)

        # Print out any errors/warnings that we encountered.
        self._print_errors()

    def _process_field(self, tag, arg, descr, warnings):
        if tag in ('variable', 'var'):
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            if self._tmp_var.has_key(arg):
                warnings.append('Redefinition of @%s %s' % (tag, arg))
            self._tmp_var[arg] = descr
        elif tag == 'type':
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            args = re.split('[:;, ] *', arg.strip())
            for arg in args:
                if self._tmp_type.has_key(arg):
                    warnings.append('Redefinition of @%s %s' % (tag, arg))
                self._tmp_type[arg] = descr
        else:
            ObjDoc._process_field(self, tag, arg, descr, warnings)

    def __repr__(self):
        str = '<ModuleDoc: '+`self._uid`+' ('
        if (not self._modules and not self._classes and
            not self._functions and not self._variables):
            return str[:-2]+'>'
        if self._modules:
            str += `len(self._modules)`+' modules; '
        if self._classes:
            str += `len(self._classes)`+' classes; '
        if self._functions:
            str += `len(self._functions)`+' functions; '
        if self._variables:
            str += `len(self._variables)`+' variables; '
        return str[:-2]+')>'

    #////////////////////////////
    #// Import Discovery
    #////////////////////////////

    _EXPR_STMT_PATTERN = (symbol.stmt,
                         (symbol.simple_stmt,
                          (symbol.small_stmt,
                           ['expr_stmt']),
                          (token.NEWLINE, '')))
    _LHS_PATTERN = (symbol.test,
             (symbol.and_test,
              (symbol.not_test,
               (symbol.comparison,
                (symbol.expr,
                 (symbol.xor_expr,
                  (symbol.and_expr,
                   (symbol.shift_expr,
                    (symbol.arith_expr,
                     (symbol.term,
                      (symbol.factor,
                       (symbol.power,
                        ['lhs_atom']))))))))))))
    
    def _find_imported_variables(self):
        """
        If possible, read the module's python documentation, and try
        to figure out which variables were imported.  To decide which
        variables were imported, we use the following heuristic: if a
        variable isn't defined in the lhs of a top-level statement,
        then it's probably imported.  Note that this isn't 100%
        effective.  E.g., if someone uses C{global}, uses a
        C{try}/C{except} block, or directly modifies their modules'
        C{__dict__}, then we'll be fooled.

        @bug: If you define class C with method g, and then at the
            top level you say 'h=C.g', then this method will think
            that is an imported variable (since the var's name
            is derived from its uid, and the uid will be C.g, not
            h).
        """
        # Don't bother if there aren't any variables, anyway.
        if len(self._variables) == 0: return
        
        # Get the filename of the module.
        try: filename = self._uid.value().__file__
        except: return # E.g., a builtin module.
        if filename[-4:-1].lower() == '.py':
            filename = filename[:-1]
    
        # Parse the module's source code.
        try:
            source_code = open(filename).read()
            ast = parser.suite(source_code.replace('\r\n','\n')+'\n')
        except:
            return # E.g., a .pyc with no corresponding .py
    
        # Construct a list of defined variables.  To do this, we search
        # the parse tree for statements of the form "a=b" or "a=b=c" or
        # "a,b=c" or "(a,b)=c" or "[a,b]=c".
        defined_variables = {}
        for stmt in ast.totuple()[1:]:
            dict = {}
            if _ast_match(self._EXPR_STMT_PATTERN, stmt, dict)[0]:
                expr_stmt = dict['expr_stmt']
                for i in range(2, len(expr_stmt)):
                    if expr_stmt[i] == (token.EQUAL, '='):
                        lhs = expr_stmt[i-1]
                        self._find_defined_vars(lhs, defined_variables)

        # Any explicit (non-autogenerated) variables are also
        # considierd to be "defined."
        for var in self._variables:
            if not var.autogenerated():
                defined_variables[var.name()] = 1

        # Go through our list of variables.  Move any variables that
        # were *not* defined by a top-level statement to the
        # imported_variables list.
        self._imported_variables = [v for v in self._variables
                                    if not defined_variables.has_key(v.name())]
        self._variables = [v for v in self._variables
                           if defined_variables.has_key(v.name())]

    def _find_defined_vars(self, lhs_testlist, defined_variables):
        """
        A helper function for L{_find_imported_variables}: for every
        variable C{M{v}} set by the given left-hand-side of an
        equation, set C{defined_variables[M{v}]=1}.

        @param lhs_testlist: An AST tuple containing a C{testlist}
            element from the left hand side of an equation.
        @param defined_variables: The output dictionary.    
        """
        # First, make sure it's a testlist (or a listmaker, for things
        # like [a,b] = 12)
        if lhs_testlist[0] not in (symbol.testlist, symbol.listmaker): return
    
        # Traverse the AST, looking for variables.
        dict = {}
        for lhs_test in lhs_testlist[1:]:
            if _ast_match(self._LHS_PATTERN, lhs_test, dict)[0]:
                lhs_atom = dict['lhs_atom']
                if lhs_atom[0] != symbol.atom: continue
                for lhs in lhs_atom[1:]:
                    if lhs[0] == token.NAME:
                        # A variable on the LHS
                        defined_variables[lhs[1]] = 1
                    elif lhs[0] in (symbol.testlist, symbol.listmaker):
                        # A tuple pattern on the LHS
                        self._find_defined_vars(lhs, defined_variables)
            
    #////////////////////////////
    #// Accessors
    #////////////////////////////

    def functions(self):
        """
        @return: A list of all functions defined by the
            module/package documented by this C{ModuleDoc}.
        @rtype: C{list} of L{Link}
        """
        return self._functions
    
    def classes(self):
        """
        @return: A list of all classes defined by the
            module/package documented by this C{ModuleDoc},
            sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._classes
    
    def variables(self):
        """
        @return: A list of all variables defined by the
            module/package documented by this C{ModuleDoc},
            sorted by name.
        @rtype: C{list} of L{Var}
        """
        return self._variables

    def imported_modules(self):
        """
        @return: A list of all modules that are imported by the
            module/package documented by this C{ModuleDoc},
            sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._imported_modules
    
    def imported_functions(self):
        """
        @return: A list of all functions contained in the
            module/package documented by this C{ModuleDoc} that are
            not defined by that module/package, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._imported_functions
    
    def imported_classes(self):
        """
        @return: A list of all classes contained in the
            module/package documented by this C{ModuleDoc} that are
            not defined by that module/package, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._imported_classes

    def imported_variables(self):
        """
        @return: A list of all variables contained in the
            module/package documented by this C{ModuleDoc} that are
            not defined by that module/package, sorted by name.
        @rtype: C{list} of L{Var}
        """
        return self._imported_variables
    
    def package(self):
        """
        @return: The package that contains the module documented by
            this C{ModuleDoc}, or C{None} if no package contains the
            module. 
        @rtype: L{UID} or C{None}
        """
        return self._uid.package()    

    def ispackage(self):
        """
        @return: True if this C{ModuleDoc} documents a package (not a
            module). 
        @rtype: C{boolean}
        """
        return self._modules != None
    
    def ismodule(self):
        """
        @return: True if this C{ModuleDoc} documents a module (not a
            package). 
        @rtype: C{boolean}
        """
        return self._modules == None
    
    def modules(self):
        """
        @return: A list of the known modules and subpackages conained
            in the package documented by this C{ModuleDoc}, sorted by
            name.
        @rtype: C{list} of L{Link}
        @raise TypeError: If this C{ModuleDoc} does not document a
            package. 
        """
        if self._modules == None:
            raise TypeError('This ModuleDoc does not '+
                            'document a package.')
        return self._modules
    
    #////////////////////////////
    #// Modifiers
    #////////////////////////////

    def remove_autogenerated_variables(self):
        self._variables = [v for v in self._variables
                           if not v.autogenerated()]

    def add_modules(self, modules):
        """
        Register submodules for the package doumented by this
        C{ModuleDoc}.  This must be done externally, since we can't
        determine the submodules of a package through introspection
        alone.  This is automatically called by L{DocMap.add} when new
        modules are added to a C{DocMap}.

        @param modules: A list of modules or subpackages.
        @type modules: C{list} of L{UID}
        @rtype: C{None}
        """
        if self._modules == None:
            raise TypeError('This ModuleDoc does not '+
                            'document a package.')
        for module in modules:
            name = (module.__name__.split('.'))[-1]
            uid = make_uid(module, self._uid, name)
            self._modules.append(Link(name, uid))

        # Make sure the modules stay sorted.
        self._modules = self._sort(self._modules)

#////////////////////////////////////////////////////////
#// ClassDoc
#////////////////////////////////////////////////////////
class ClassDoc(ObjDoc):
    """
    The documentation for a class.  This documentation consists of
    standard pieces of documentation (as defined in L{ObjDoc}), and
    the following class-specific pieces of documentation:
    
        - X{bases}: A list of the class's base classes.
        - X{subclasses}: A list of the class's known subclasses.
        - X{methods}: A list of the methods defined by the class.
        - X{staticmethods}: A list of static methods defined by
          the class.
        - X{classmethods}: A list of class methods defined by the
          class.
        - X{properties}: A list of properties defined by the class.
        - X{ivariables}: A list of the instance variables defined by the
          class.
        - X{cvariables}: A list of the class variables defined by the
          class.
        - X{module}: The module that defines the class.

    @group Accessors: is_exception, methods, classmethods,
        staticmethods, properties, allmethods, cvariables,
        ivariables, bases, subclasses, property_type, base_order
    @group Inheritance: add_subclasses, inherit, _inherit_vars,
        _inherit_groups, _add_inheritance_groups

    @type _methods: C{list} of L{Link}
    @ivar _methods: A list of all methods contained in this class. 

    @type _ivariables: C{list} of L{Var}
    @ivar _ivariables: A list of all instance variables defined by this 
        class.
    
    @type _cvariables: C{list} of L{Var}
    @ivar _cvariables: A list of all class variables defined by this 
        class.

    @type _bases: C{list} of L{Link}
    @ivar _bases: A list of the identifiers of this class's bases.
    """
    def __init__(self, uid, verbosity=0):
        cls = uid.value()

        # Variables:
        self._tmp_ivar = {}
        self._tmp_cvar = {}
        self._tmp_type = {}
        self._property_type = {}

        ObjDoc.__init__(self, uid, verbosity)

        # Handle methods & class variables
        self._methods = []
        self._cvariables = []
        self._ivariables = []
        self._staticmethods = []
        self._classmethods = []
        self._properties = []

        # Find the order that bases are searched in.
        base_order = _find_base_order(cls)
        self._base_order = [make_uid(b) for b in base_order]

        try: fields = dir(cls)
        except: fields = []
        for field in fields:
            # Don't do anything for these special variables:
            if field in ('__doc__', '__module__', '__dict__', '__weakref__'):
                continue

            # Find the class that defines the field; and get the value
            # directly from that class (so methods & variables have
            # the right uids).
            (val, container) = _lookup_class_field(cls, field, base_order)

            linkname = field
            private_prefix = '_%s__' % container.shortname()
            if field.startswith(private_prefix):
                if container == self._uid:
                    # If it's private and belongs to this class, then
                    # undo the private name mangling.
                    linkname = linkname[len(private_prefix)-2:]
                else:
                    # If it's private, and belongs to a parent class,
                    # then don't even list it here.
                    continue

            # Deal with static/class methods and properties. (Python 2.2)
            try:
                # Get the un-munged value.
                try: rawval = container.value().__dict__.get(field)
                except: pass

                if isinstance(rawval, staticmethod):
                    vuid = make_uid(rawval, container, linkname)
                    vlink = Link(linkname, vuid)
                    self._staticmethods.append(vlink)
                    continue
                elif isinstance(rawval, classmethod):
                    vuid = make_uid(rawval, container, linkname)
                    vlink = Link(linkname, vuid)
                    self._classmethods.append(vlink)
                    continue
                elif isinstance(rawval, property):
                    vuid = make_uid(rawval, container, linkname)
                    vlink = Link(linkname, vuid)
                    self._properties.append(vlink)
                    continue
            except NameError: pass
                
            # Create a UID and Link for the field value.
            vuid = make_uid(val, container, linkname)
            vlink = Link(linkname, vuid)

            # Don't do anything if it doesn't have a full-path UID.
            if vuid is None: continue
            # Don't do anything for modules.
            if vuid.is_module(): continue

            # Is it a method?
            if vuid.is_routine():
                self._methods.append(vlink)

            elif container == self._uid:
                # Is it an instance variable?
                if self._tmp_ivar.has_key(field):
                    descr = self._tmp_ivar[field]
                    del self._tmp_ivar[field]
                    typ = self._tmp_type.get(field)
                    if typ is not None: del self._tmp_type[field]
                    else: typ = markup.parse_type_of(val)
                    self._ivariables.append(Var(field, vuid, descr, typ, 1))
                    
                # Is it a class variable?
                else:
                    autogen = 1 # is it autogenerated?
                    descr = self._tmp_cvar.get(field)
                    if descr is not None:
                        del self._tmp_cvar[field]
                        autogen = 0
                    typ = self._tmp_type.get(field)
                    if typ is not None:
                        del self._tmp_type[field]
                        autogen = 0
                    else: typ = markup.parse_type_of(val)
                    self._cvariables.append(Var(field, vuid, descr,
                                                typ, 1, autogen))

        # Keep track of types for properties.
        for prop in self._properties:
            name = prop.name()
            typ = self._tmp_type.get(name)
            if typ is not None:
                if prop.target().cls() != self._uid:
                    estr = "@type can't be used on an inherited properties"
                    self._field_warnings.append(estr)
                self._property_type[prop.target()] = typ
                del self._tmp_type[name]

        # Add the remaining class variables
        for (name, descr) in self._tmp_cvar.items():
            typ = self._tmp_type.get(name)
            if typ is not None: del self._tmp_type[name]
            vuid = make_uid(None, self._uid, name)
            self._cvariables.append(Var(name, vuid, descr, typ, 0))

        # Add the instance variables.
        for (name, descr) in self._tmp_ivar.items():
            typ = self._tmp_type.get(name)
            if typ is not None: del self._tmp_type[name]
            vuid = make_uid(None, self._uid, name)
            self._ivariables.append(Var(name, vuid, descr, typ, 0))

        # Make sure we used all the type fields.
        if self._tmp_type:
            for key in self._tmp_type.keys():
                estr = '@type for unknown variable %s' % key
                self._field_warnings.append(estr)
        del self._tmp_ivar
        del self._tmp_cvar
        del self._tmp_type

        # Add links to base classes.
        try: bases = cls.__bases__
        except AttributeError: bases = []
        self._bases = [Link(base.__name__, make_uid(base)) for base in bases
                       if (type(base) in (types.ClassType, _ZopeType) or
                           (isinstance(base, types.TypeType)))]

        # Initialize subclass list.  (Subclasses get added
        # externally with add_subclass())
        self._subclasses = []

        # Is it an exception?
        try: self._is_exception = issubclass(cls, Exception)
        except TypeError: self._is_exception = 0

        # Inherited variables (added externally with inherit())
        self._inh_cvariables = []
        self._inh_ivariables = []

        # Assemble a list of all methods
        self._allmethods = (self._methods + self._classmethods +
                            self._staticmethods)

        # Put everything in sorted order.
        self._methods = self._sort(self._methods)
        self._classmethods = self._sort(self._classmethods)
        self._staticmethods = self._sort(self._staticmethods)
        self._properties = self._sort(self._properties)
        self._cvariables = self._sort(self._cvariables)
        self._ivariables = self._sort(self._ivariables)
        self._bases = self._sort(self._bases)
        self._subclasses = self._sort(self._subclasses)
        self._allmethods = self._sort(self._allmethods)
        
        # Print out any errors/warnings that we encountered.
        self._print_errors()

    def _process_field(self, tag, arg, descr, warnings):
        if tag in ('cvariable', 'cvar'):
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            if self._tmp_cvar.has_key(arg):
                warnings.append('Redefinition of @%s %s' % (tag, arg))
            self._tmp_cvar[arg] = descr
        elif tag in ('ivariable', 'ivar'):
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            if self._tmp_ivar.has_key(arg):
                warnings.append('Redefinition of @%s %s' % (tag, arg))
            self._tmp_ivar[arg] = descr
        elif tag == 'type':
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            args = re.split('[:;, ]+ *', arg.strip())
            for arg in args:
                if self._tmp_type.has_key(arg):
                    warnings.append('Redefinition of @%s %s' % (tag, arg))
                self._tmp_type[arg] = descr
            self._tmp_type[arg] = descr
        else:
            ObjDoc._process_field(self, tag, arg, descr, warnings)

    def __repr__(self):
        str = '<ClassDoc: '+`self._uid`+' ('
        if (not self._bases and not self._methods and
            not self._cvariables and not self._ivariables and
            not self._subclasses):
            return str[:-2]+'>'
        if self._bases:
            str += `len(self._bases)`+' base classes; '
        if self._methods:
            str += `len(self._methods)`+' methods; '
        if self._cvariables:
            str += `len(self._cvariables)`+' class variabless; '
        if self._ivariables:
            str += `len(self._ivariables)`+' instance variables; '
        if self._subclasses:
            str += `len(self._subclasses)`+' subclasses; '
        return str[:-2]+')>'

    #////////////////////////////
    #// Accessors
    #////////////////////////////

    def is_exception(self):
        """
        @return: True if this C{ClassDoc} documents an exception
            class. 
        @rtype: C{boolean}
        """
        return self._is_exception

    def methods(self):
        """
        @return: A list of all (instance) methods defined by the
            class documented by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._methods
    
    def classmethods(self):
        """
        @return: A list of all class methods defined by the class
            documented by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._classmethods
    
    def staticmethods(self):
        """
        @return: A list of all static methods defined by the class
            documented by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._staticmethods

    def properties(self):
        """
        @return: A list of all properties defined by the class
            documented by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._properties

    def allmethods(self):
        """
        @return: A list of all instance, class, and static methods
            defined by the class documented by this C{ClassDoc},
            sorted by name.
        @rtype: C{list} of L{Link}
        @see: L{methods}
        @see: L{staticmethods}
        @see: L{classmethods}
        """
        return self._allmethods
    
    def cvariables(self):
        """
        @return: A list of all class variables defined by the class
            documented by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Var}
        """
        return self._cvariables
    
    def ivariables(self):
        """
        @return: A list of all instance variables defined by the class
            documented by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Var}
        """
        return self._ivariables

    def bases(self):
        """
        @return: A list of all base classes for the class documented
            by this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._bases
    
    def subclasses(self):
        """
        @return: A list of known subclasses for the class documented by
            this C{ClassDoc}, sorted by name.
        @rtype: C{list} of L{Link}
        """
        return self._subclasses
    
    def property_type(self, uid):
        """
        @return: The type for the given property, as specified by the
            docstring of the class documented by this C{ClassDoc}.  If
            the docstring doesn't specify a type, then C{None} is
            returned.  (But note that the property can also specify
            its type in its own docstring).
        @rtype: L{markup.ParsedDocstring} or C{None}
        """
        return self._property_type.get(uid, None)

    def base_order(self):
        """
        @return: A list of all base ancestors of the class documented
            by this C{ClassDoc}, listed in the order that they are
            searched by C{Python} for members.  The first element of
            this list will always be the C{UID} of the class documented
            by this C{ClassDoc}.
        @rtype: C{list} of L{UID}
        """
        return self._base_order

    #////////////////////////////
    #// Modifiers
    #////////////////////////////

    def add_subclasses(self, classes):
        """
        Register subclasses for the class doumented by this
        C{ClassDoc}.  This must be done externally, since we can't
        determine a class's subclasses through introspection
        alone.  This is automatically called by L{DocMap.add} when new
        classes are added to a C{DocMap}.

        @param classes: A list of subclasses.
        @type classes: C{list} of L{UID}
        @rtype: C{None}
        """
        for cls in classes:
            cuid = make_uid(cls, self._uid, cls.__name__)
            self._subclasses.append(Link(cls.__name__, cuid))
        # Keep the list sorted.
        self._subclasses = self._sort(self._subclasses)

    #////////////////////////////
    #// Inheritance
    #////////////////////////////

    def inherit(self, base_docs, inheritance_groups,
                inherit_groups):
        """
        Add inherited variables and groups to this C{ClassDoc}.  To
        search for inherited variables and groups, C{inherit} uses a
        list of the documentation objects for every base ancestor of
        the class documented by this C{ClassDoc}.  Typical usage is:

            >>> doc.inherit([docmap[b] for b in doc.base_order()])

        @type base_docs: C{list} of L{ClassDoc}
        @param base_docs: A list of the C{ClassDoc}s for every base
            ancestor of the class documented by this C{ClassDoc}.
            These should be the C{ClassDoc}s for the classes returned
            by L{base_order}, in the order that they are returned by
            L{base_order}.
        @type inheritance_groups: C{boolean}
        @param inheritance_groups: If true, then create a group for
            each base ancestor, containing the members that are
            inherited from that base.  These groups have names of
            the form C{'Inherited from M{base}'}.
        @type inherit_groups: C{boolean}
        @param inherit_groups: If true, then inherit groups from the
            base ancestors.
        @rtype: C{None}
        """
        self._inherit_vars(base_docs)
        if inheritance_groups:
            self._add_inheritance_groups()
        if inherit_groups:
            self._inherit_groups(base_docs)

    def _inherit_vars(self, base_docs):
        """
        Add inherited class and instance variables to this
        C{ClassDoc}.  To search for inherited variables,
        C{inherit_vars} uses a list of the documentation objects for
        every base ancestor of the class documented by this
        C{ClassDoc}.

        @type base_docs: C{list} of L{ClassDoc}
        @param base_docs: A list of the C{ClassDoc}s for every base
            ancestor of the class documented by this C{ClassDoc}.
            These should be the C{ClassDoc}s for the classes returned
            by L{base_order}, in the order that they are returned by
            L{base_order}.
        @rtype: C{None}
        """
        # Use these dictionaries to assign a Var to each variable
        # name.  The values of these dictionaries will give us the
        # ivariables and cvariables lists, when we're done.
        ivariables_map = {}
        cvariables_map = {}
        for ivar in self._ivariables:
            ivariables_map[ivar.name()] = ivar
        for cvar in self._cvariables:
            cvariables_map[cvar.name()] = cvar

        # Skip base_docs[0], since that's ourselves.
        for base_doc in base_docs[1:]:
            if base_doc is None: continue

            # Inherit instance variables.
            for base_ivar in base_doc.ivariables():
                # Ignore ivars that the base inherited from higher up
                if base_ivar.uid().cls() != base_doc.uid(): continue
                if base_ivar.uid().cls() is None: continue
                
                name = base_ivar.name()
                if ivariables_map.has_key(name):
                    # We already have this ivariable.
                    pass
                elif cvariables_map.has_key(name):
                    # We have it listed as a cvariable.  If the cvar
                    # was autogenerated, then merge it with base_ivar,
                    # and list it under ivariables; otherwise, leave
                    # it as is.
                    cvar = cvariables_map[name]
                    if cvar.autogenerated():
                        ivar = Var(name, cvar.uid(), base_ivar.descr(), 
                                   base_ivar.type(), cvar.has_value(), 0)
                        ivariables_map[name] = ivar
                        del cvariables_map[name]
                else:
                    # Copy the ivariable from the base class.
                    ivariables_map[name] = base_ivar

            # Update class variables.
            for base_cvar in base_doc.cvariables():
                # Ignore cvars that the base inherited from higher up
                if base_cvar.uid().cls() != base_doc.uid(): continue
                if base_cvar.uid().cls() is None: continue
                
                name = base_cvar.name()
                if ivariables_map.has_key(name):
                    # We already have this listed as an ivariable.
                    pass
                elif cvariables_map.has_key(name):
                    # We already have this cvariable.  But if our cvar
                    # was autogenerated, then merge it with base_cvar.
                    cvar = cvariables_map[name]
                    if cvar.autogenerated() and not base_cvar.autogenerated():
                        cvar = Var(name, cvar.uid(), base_cvar.descr(),
                                   base_cvar.type(), cvar.has_value, 0)
                        cvariables_map[name] = cvar
                else:
                    # Copy the cvariable from the base class.
                    cvariables_map[name] = base_cvar

        # Extract the variable lists from the maps.
        self._ivariables = ivariables_map.values()
        self._cvariables = cvariables_map.values()

        # Keep the lists of variables sorted
        self._ivariables = self._sort(self._ivariables)
        self._cvariables = self._sort(self._cvariables)

    def _inherit_groups(self, base_docs):
        """
        Inherit groups from the given list of C{ClassDoc}s.  These
        should be the C{ClassDoc}s for the classes returned by
        L{base_order}, in the order that they are returned by
        L{base_order}.

        @type base_docs: C{list} of L{ClassDoc}
        @param base_docs: The documentation for the 
        @rtype: C{None}
        """
        groupnames = self._groupnames
        name2groupnum = self._name2groupnum
        group2groupnum = self._group2groupnum
        
        # Skip base_docs[0], since that's ourselves.
        for base_doc in base_docs[1:]:
            if base_doc is None: continue
            base_groupnames = base_doc._groupnames

            # Add any new groupnames.
            for groupname in base_groupnames:
                if not group2groupnum.has_key(groupname):
                    groupnames.append(groupname)
                    group2groupnum[groupname] = len(groupnames)-1

            # Copy group membership information from the base class.
            for (name, num) in base_doc._name2groupnum.items():
                name2groupnum[name] = group2groupnum[base_groupnames[num]]

            # Copy any regexp groups
            self._regexp_groups += base_doc._regexp_groups
                
    def _add_inheritance_groups(self):
        """
        For each base class that this class inherits from, create a
        group that includes all methods, properties, and variables
        that were inherited from that base.
        """
        inherit_from = {}  # which bases did we inherit something from?
        name2base = {}     # what base does each name come from?
        base2groupnum = {} # what groupnum is used for each base?

        # Check to make sure we're not klobbering any groups.
        groupnames = self._groupnames
        if len(groupnames) > 1:
            for groupname in groupnames[1:]:
                if groupname.startswith('Inherited'):
                    estr = '"Inherited..." is a reserved group name.'
                    self._field_warnings.append(estr)

        # Find the base that each method & property inherits from.
        for link in self.allmethods()+self.properties():
            uid = link.target()
            cls = uid.cls()
            if cls is None: continue
            if cls != self._uid:
                name2base[link.name()] = cls
                inherit_from[cls] = 1

        # Find the base that each variable inherits from.
        for var in self.ivariables()+self.cvariables():
            uid = var.uid()
            cls = uid.cls()
            if cls is None: continue
            if cls != self._uid:
                name2base[var.name()] = cls
                inherit_from[cls] = 1

        # If we didn't inherit anything, then we're done.
        if len(inherit_from) == 0: return

        # Add an inheritance group for every base class that we
        # inherited something from.  Include them in the order given
        # by _base_order.
        group2groupnum = self._group2groupnum
        for base in self._base_order:
            if inherit_from.has_key(base):
                groupname = 'Inherited from %s' % base.shortname()
                groupnames.append(groupname)
                groupnum = len(groupnames)-1
                base2groupnum[base] = groupnum
                group2groupnum[groupname] = groupnum
                del inherit_from[base]
                
        # There shouldn't be anything left, but just in case...
        for base in inherit_from.keys():
            groupnames.append('Inherited from %s' % base.shortname())
            base2groupnum[base] = len(groupnames)-1

        # Now that we have groupnums for each base, we can add all the
        # names to _name2groupnum.
        name2groupnum = self._name2groupnum
        for (name, group) in name2base.items():
            name2groupnum[name] = base2groupnum[name2base[name]]

#////////////////////////////////////////////////////////
#// FuncDoc
#////////////////////////////////////////////////////////
class FuncDoc(ObjDoc):
    """
    The documentation for a function.  This documentation consists of
    standard pieces of documentation (as defined in L{ObjDoc}), and
    the following function-specific pieces of documentation:

        - X{parameters}: A list of the function's positional
          parameters.
        - X{vararg}: The function's vararg parameter, or C{None}.
        - X{kwarg}: The function's keyword parameter, or C{None}.
        - X{returns}: The function's return value.
        - X{raises}: A list of exceptions that may be raised by the
          function.
        - X{overrides}: The method that this method overrides.

    @group Accessors: parameters, vararg, kwarg, returns, raises,
        overrides, matches_override, parameter_list
    @group Inheritance: find_override
    @group Error Reporting: _param_mismatches
    @group Signature Parsing: _init_signature,
        _params_to_vars, _signature_match, _init_builtin_signature

    @type _params: C{list} of L{Var}
    @ivar _params: A list of this function's normal parameters.
    @type _vararg_param: L{Var}
    @ivar _vararg_param: This function's vararg parameter, or C{None}
        if it has none.
    @type _kwarg_param: L{Var}
    @ivar _kwarg_param: This function's keyword parameter, or C{None}
        if it has none.
    @type _return: L{Var}
    @ivar _return: This function's return value.
    @type _raises: C{list} of L{Raise}
    @ivar _raises: The exceptions that may be raised by this
        function.
    @cvar _SIGNATURE_RE: A regular expression that is used to check
        whether a builtin function or method has a signature in its
        docstring.
    @cvar _param_mismatches: A dictionary whose keys are pairs
        (uid, base_uid), where uid is a method whose parameters
        do not match the parameters of its base class base_uid.
    """
    _param_mismatches = []

    def __init__(self, uid, verbosity=0):
        func = uid.value()
        
        #self._tmp_param = {}
        #self._tmp_type = {}
        self._keywords = []
        
        self._raises = []
        self._overrides = None
        self._matches_override = 0
        docstring = _getdoc(func)

        # Initialize the signature
        if uid.is_method(): func = func.im_func
        if type(func) is types.FunctionType:
            self._init_signature(func)
        elif uid.is_routine():
            # If there's a builtin signature, then just parse it;
            # don't include it in the description.
            if self._init_builtin_signature(func):
                if '\n' in docstring:
                    docstring = docstring.split('\n', 1)[1]
        else:
            raise TypeError("Can't document %s" % func)

        # These are used to keep track of params & keyword arguments
        # while processing fields; we can delete them when we're done.
        self._tmp_param = {}
        self._tmp_keyword = {}
        self._tmp_extra_type = {} # types for @keyword args
        for param in self.parameter_list():
            self._tmp_param[param.name()] = param

        # Parse the docstring.
        ObjDoc.__init__(self, uid, verbosity, docstring)

        # Deal with any extra types for @keyword args
        for (name, typ) in self._tmp_extra_type.items():
            kwarg = self._tmp_keyword.get(name)
            if kwarg is None:
                estr = '@type for unknown parameter %s' % name
                self._field_warnings.append(estr)
            else:
                kwarg.set_type(typ)

        # We're done processing fields; we don't need these anymore.
        del self._tmp_param, self._tmp_keyword, self._tmp_extra_type

        # Print out any errors/warnings that we encountered.
        self._print_errors()

    # The regular expression that is used to check whether a builtin
    # function or method has a signature in its docstring.  Err on the
    # side of conservatism in detecting signatures.
    _SIGNATURE_RE = re.compile(
        # Class name (for builtin methods)
        r'^\s*((?P<self>\w+)\.)?' +
        
        # The function name (must match exactly)
        r'(?P<func>\w+)' +
        
        # The parameters
        r'\((?P<params>(\s*\[?\s*[\w\-\.]+(=.+?)?'+
        r'(\s*\[?\s*,\s*\]?\s*[\w\-\.]+(=.+?)?)*\]*)?)\s*\)' +
        
        # The return value (optional)
        r'(\s*(->|<=+>)\s*(?P<return>\S.*?))?'+
        
        # The end marker
        r'\s*(\n|\s+--\s+|$|\.\s|\.\n)')
        
    def _init_builtin_signature(self, func):
        """
        Construct the signature for a builtin function or method from
        its docstring.  If the docstring uses the standard convention
        of including a signature in the first line of the docstring
        (and formats that signature according to standard
        conventions), then it will be used to extract a signature.
        Otherwise, the signature will be set to a single varargs
        variable named C{"..."}.

        @rtype: C{None}
        """
        self._params = []
        self._kwarg_param = None
        self._vararg_param = None
        self._return = Param('return')

        m = FuncDoc._SIGNATURE_RE.match(_getdoc(func) or '')
        if m and m.group('func') == func.__name__:
            params = m.group('params')
            rtype = m.group('return')
            selfparam = m.group('self')
            
            if selfparam and not rtype: 
                self._vararg_param = Param('...')
                return 0
            
            # Extract the parameters from the signature.
            if params:
                # Figure out which parameters are optional.
                while '[' in params or ']' in params:
                    m2 = re.match(r'(.*)\[([^\[\]]+)\](.*)', params)
                    if not m2:
                        self._vararg_param = Param('...')
                        return 0
                    (start, mid, end) = m2.groups()
                    mid = re.sub(r'((,|^)\s*[\w\-\.]+)', r'\1=...', mid)
                    params = start+mid+end

                params = re.sub(r'=...=' , r'=', params)
                for name in params.split(','):
                    if '=' in name: (name, default) = name.split('=',1)
                    else: default = None
                    name = name.strip()
                    if name == '...':
                        self._vararg_param = Param('...', default=default)
                    elif name.startswith('**'):
                        self._kwarg_param = Param(name[1:], default=default)
                    elif name.startswith('*'):
                        self._vararg_param = Param(name[1:], default=default)
                    else:
                        self._params.append(Param(name, default=default))

            # Extract the return type/value from the signature
            if rtype:
                if selfparam: self._return.set_descr(markup.parse(rtype))
                else: self._return.set_type(markup.parse(rtype, verbatim=0))

            # Add the self parameter, if it was specified.
            if selfparam:
                self._params.insert(0, Param(selfparam))

            # We found a signature.
            return 1
            
        else:
            # We couldn't parse the signature.
            self._vararg_param = Param('...')
            return 0

    def _init_signature(self, func):
        # Get the function's signature
        (args, vararg, kwarg, defaults) = inspect.getargspec(func)

        # Construct argument/return Variables.
        self._params = self._params_to_vars(args, defaults)
        if vararg: self._vararg_param = Param(vararg)
        else: self._vararg_param = None
        if kwarg: self._kwarg_param = Param(kwarg)
        else: self._kwarg_param = None
        self._return = Param('return')

    def _params_to_vars(self, params, defaults):
        vars = []
        if defaults == None: defaults = []
        for i in range(len(params)):
            try: defaultindex = i-(len(params)-len(defaults))
            except TypeError:
                # We couldn't figure out how to line up defaults with vars.
                defaultindex=-1
            if type(params[i]) is types.StringType:
                vars.append(Param(params[i]))
                if defaultindex >= 0:
                    try: vars[-1].set_default(`defaults[defaultindex]`)
                    except: vars[-1].set_default('...')
            elif defaultindex >= 0:
                vars.append(self._params_to_vars(params[i],
                                                 defaults[defaultindex]))
            else:
                vars.append(self._params_to_vars(params[i], []))
        return vars

    def _signature_match(self, basespec, childspec):
        """
        @rtype: C{boolean}
        @return: True if the signature of C{childfunc} matches the
        signature of C{basefunc} well enough that we should inherit
        its documentation.
        """
        (b_arg,b_vararg,b_kwarg,b_default) = basespec
        (c_arg,c_vararg,c_kwarg,c_default) = childspec
        b_default = b_default or ()
        c_default = c_default or ()
        
        # The child method must define all arguments that the base
        # method does.
        if b_arg != c_arg[:len(b_arg)]:
            return 0

        # Any arg that's not defined by the base method must have a
        # default value in the child method; and any arg that has a
        # default value in the base method must have a default value
        # in the child method.
        if (len(b_arg)-len(b_default)) < (len(c_arg)-len(c_default)):
            return 0

        # Varargs must match; but the child method may add a varargs,
        # if the base method doesn't have one.
        if b_vararg is not None and b_vararg != c_vararg:
            return 0
    
        # Kwargs must match; but the child method may add a kwargs,
        # if the base method doesn't have one.
        if b_kwarg is not None and b_kwarg != c_kwarg:
            return 0

        # Otherwise, they match.
        return 1

    def _process_field(self, tag, arg, descr, warnings):
        if tag in ('return', 'returns'):
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            if self._return.descr() is not None:
                warnings.append('Redefinition of @%s' % tag)
            self._return.set_descr(descr)
        elif tag in ('returntype', 'rtype'):
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            if self._return.type() is not None:
                warnings.append('Redefinition of @%s' % tag)
            self._return.set_type(descr)
        elif tag in ('param', 'parameter', 'arg', 'argument'):
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            if not re.search('[:;, ]', arg):
                # Normal case: descr for a single parameter
                param = self._tmp_param.get(arg)
                if param is None:
                    warnings.append('@%s for unknown parameter %s' % (tag,arg))
                else:
                    if param.descr() is not None:
                        warnings.append('Redefinition of parameter %s' % arg)
                    elif param.listed_under() is not None:
                        warnings.append('Redefinition of parameter %s' % arg)
                        old_parent = param.listed_under()
                        old_parent.remove_shared_descr_param(param)
                    param.set_descr(descr)
            else:
                # Special case: multiple parameters listed at once.
                param_names = re.split('[:;, ]+ *', arg.strip())

                # First, filter out any params that don't exist, and
                # issue warnings accordingly.
                params = [self._tmp_param.get(p) for p in param_names]
                for (param, name) in zip(params, param_names):
                    if param is None:
                        warnings.append('@%s for unknown parameter %s' %
                                        (tag, name))
                    elif param.descr() is not None:
                        warnings.append('Redefinition of parameter %s' % name)
                        param.set_descr(None)
                params = [p for p in params if p is not None]
                if not params: return
                
                # List the description & extra params under the param
                # that occurs first.
                for param1 in self.parameter_list():
                    for param2 in params:
                        if param1 == param2:
                            param1.set_descr(descr)
                            shared = [p for p in params if p != param1]
                            param1.set_shared_descr_params(shared)
                            return

        elif tag == 'type':
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            args = re.split('[:;, ]+ *', arg.strip())
            for arg in args:
                param = self._tmp_param.get(arg)
                if param is None:
                    if self._tmp_extra_type.has_key(arg):
                        warnings.append('Redefinition of @%s %s' % (tag,arg))
                    self._tmp_extra_type[arg] = descr
                else:
                    if param.type() is not None:
                        warnings.append('Redefinition of @%s %s' % (tag,arg))
                    param.set_type(descr)
        elif tag in ('keyword', 'kwarg', 'kwparam'):
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            if self._tmp_param.has_key(arg):
                warnings.append('@%s used for explitict parameter %s'
                                % (tag, arg))
            if self._tmp_keyword.has_key(arg):
                warnings.append('Redefinition of parameter %s' % arg)
            self._keywords.append(Param(arg, descr))
            self._tmp_keyword[arg] = self._keywords[-1]
        elif tag in ('raise', 'raises', 'exception', 'except'):
            if arg is None:
                warnings.append(tag+' expected a single argument')
                return
            self._raises.append(Raise(arg, descr))
        else:
            ObjDoc._process_field(self, tag, arg, descr, warnings)

    def __repr__(self):
        n_params = len(self._params)
        if self._vararg_param: n_params += 1
        if self._kwarg_param: n_params += 1
        str = '<FuncDoc: '+`self._uid`+' ('
        if (n_params == 0 and not self._raises):
            return str[:-2]+'>'
        if self._params:
            str += `len(self._params)`+' parameters; '
        if self._raises:
            str += `len(self._raises)`+' exceptions; '
        return str[:-2]+')>'

    #////////////////////////////
    #// Accessors
    #////////////////////////////

    def parameters(self):
        """
        @rtype: C{list} of L{Var}
        @return: The positional parameters for the function/method
            documented by this C{FuncDoc}.  This is typically a list
            of parameters, but it can contain sublists if the
            function/method's signature contains sublists.  For
            example, for the function:

                >>> def f(a, (b, c), d): pass

            For this function, C{parameters} will return a
            three-element list, whose second element is a sublist
            containing C{Var}s for C{b} and C{c}.

            If you just want a list of all parameters used by the
            function/method, use L{parameter_list} instead. 
        """
        return self._params
    
    def vararg(self):
        """
        @return: The vararg parameter for the function/method
            documented by this C{FuncDoc}, or C{None} if it has no
            vararg parameter.
        @rtype: L{Var} or C{None}
        """
        return self._vararg_param
    
    def kwarg(self):
        """
        @return: The keyword parameter for the function/method
            documented by this C{FuncDoc}, or C{None} if it has no
            keyword parameter.
        @rtype: L{Var} or C{None}
        """
        return self._kwarg_param
    
    def returns(self):
        """
        @return: The return value for the function/method
            documented by this C{FuncDoc}, or C{None} if it has no
            return value.
        @rtype: L{Var} or C{None}
        """
        return self._return

    def keywords(self):
        """
        @return: The keyword parameters for the function/method
            documented by this C{FuncDoc}.
        @rtype: C{list} of L{Var}
        """
        return self._keywords
    
    def raises(self):
        """
        @return: A list of exceptions that may be raised by the
            function/method documented by this C{FuncDoc}.
        @rtype: C{list} of L{Raise}
        """
        return self._raises
    
    def overrides(self):
        """
        @return: The method overridden by the method documented by
            this C{FuncDoc}; or C{None} if the method documented by
            this C{FuncDoc} does not override any method, or if this
            C{FuncDoc} documents a function.
        @rtype: L{Link} or C{None}
        """
        return self._overrides

    def matches_override(self):
        """
        @return: True if the method documented by this C{FuncDoc}
            overrides another method, and its signature matches the
            signature of the overridden method.
        @rtype: C{boolean}
        """
        return self._matches_override

    def parameter_list(self):
        """
        @return: A (flat) list of all parameters for the
            function/method documented by this C{FuncDoc}.  If you are
            interested in the signature of the function/method, you
            should use L{parameters} instead.  This list includes
            vararg & keyword params, but does not include the return
            value.
        @rtype: C{list} of L{Var}
        @see: L{parameters}
        """
        if not hasattr(self, '_param_list'):
            self._param_list = _flatten(self._params)
            if self._vararg_param:
                self._param_list.append(self._vararg_param)
            if self._kwarg_param:
                self._param_list.append(self._kwarg_param)
        return self._param_list

    #////////////////////////////
    #// Inheritance
    #////////////////////////////

    def find_override(self, bases):
        """
        Find the method that this method overrides.
        """
        name = self.uid().shortname()
        for base in bases[1:]:
            if base.value().__dict__.has_key(name):
                # We found a candidate for an overriden method.
                base_obj = base.value()
                try:
                    base_method = getattr(base_obj, name)
                except:
                    base_method = base_obj.__getattribute__(base_obj, name)

                # Make sure it's some kind of method.
                if type(base_method) not in (types.MethodType,
                                            types.BuiltinMethodType,
                                             _WrapperDescriptorType,
                                             _MethodDescriptorType,
                                             _ZopeMethodType,
                                             _ZopeCMethodType):
                    return

                # We've found a method that we override.  But
                # Make sure we don't override ourself.
                overrides = make_uid(base_method)
                if overrides == self._uid:
                    if sys.stderr.softspace: print >>sys.stderr
                    estr = ('Warning: %s appears to override itself' %
                            self._uid)
                    print >>sys.stderr, estr
                    return
                self._overrides = overrides

                # Get the base & child argspecs.  If either fails,
                # then one is probably a builtin of some sort, so
                # _matches_overrides should be 0 anyway.
                try:
                    basespec = inspect.getargspec(base_method.im_func)
                    childspec = inspect.getargspec(self._uid.value().im_func)
                except:
                    return

                # Does the signature of this method match the
                # signature of the method it overrides?
                if self._signature_match(basespec, childspec):
                    self._matches_override = 1
                elif name != '__init__':
                    # Issue a warning if the parameters don't match.
                    self._param_mismatches.append((self.uid(),
                                                   make_uid(base_method)))
                return

def report_param_mismatches(docmap):
    mismatches = FuncDoc._param_mismatches
    
    # Filter out any mismatches where the base method isn't
    # documented, anyway.
    mismatches = [m for m in mismatches if docmap.documented_ancestor(m[0])]
    
    if not mismatches: return 1
    mismatches.sort()
    estr = ("Warning: the following methods' parameters do not match "+
            "the parameters of the base class methods that they "+
            "override; so documentation was not inherited:")
    estr = markup.wordwrap(estr, indent=9).lstrip()
    for mismatch in mismatches:
        estr += '    - %s\n      (base method=%s)\n' % mismatch
    if sys.stderr.softspace: print >>sys.stderr
    print >>sys.stderr, estr
    return 0

#////////////////////////////////////////////////////////
#// PropertyDoc
#////////////////////////////////////////////////////////
class PropertyDoc(ObjDoc):
    """
    The documentation for a property.  This documentation consists of
    standard pieces of documentation (as defined in L{ObjDoc}), and
    the following property-specific pieces of documentation:

      - X{fget}: The property's get function
      - X{fset}: The property's set function
      - X{fdel}: The property's delete function
      - X{type}: The property's type

    @group Accessors: type, fget, fset, fdel
    """
    def __init__(self, uid, typ=None, verbosity=0):
        property = uid.value()
        cls = uid.cls()
        if property.fget is None: self._fget = None
        else: self._fget = self._make_uid(property.fget, cls, 'fget')
        if property.fset is None: self._fset = None
        else: self._fset = self._make_uid(property.fset, cls, 'fset')
        if property.fdel is None: self._fdel = None
        else: self._fdel = self._make_uid(property.fdel, cls, 'fdel')
        self._type = typ
        ObjDoc.__init__(self, uid, verbosity)

        # Print out any errors/warnings that we encountered.
        self._print_errors()

    def _make_uid(self, func, cls, functype):
        """
        Return a UID for the given fget/fset/fdel function of the
        property.  This is a heuristic function, which first looks for
        the function within the current class (and its ancestors); and
        then looks for it as a module-level function.  If the function
        is actually defined somewhere else (e.g., in an unrelated
        class), then it will be mistakenly interpreted as a module-
        level function.

        @param func: The function to get a uid for.
        @param cls: The UID for the class containing the property.
        """
        # Find the order that bases are searched in.
        base_order = _find_base_order(cls.value())
        self._base_order = [make_uid(b) for b in base_order]

        # Check if it's a method of the property's class, or any of
        # its superclasses.
        for base in base_order:
            for (field, val) in base.__dict__.items():
                if val is func:
                    container = make_uid(base)
                    try: obj = getattr(base, field)
                    except: obj = base.__getattribute__(base, field)
                    return make_uid(obj, container, field)

        # Otherwise, make a UID for it as a function (in the
        # containing module).
        try:
            return make_uid(func)
        except:
            # If we get here, then the object is most likely not a
            # function; create a fake uid for it, so we can at least
            # display some info about it.
            return make_uid(func, cls, '-%s-' % functype)

    def _process_field(self, tag, arg, descr, warnings):
        if tag == 'type':
            if arg is not None:
                warnings.append(tag+' did not expect an argument')
                return
            if self._type is not None:
                warnings.append('Redefinition of %s' % tag)
            self._type = descr
        else:
            ObjDoc._process_field(self, tag, arg, descr, warnings)

    def __repr__(self):
        return '<PropertyDoc: %s>' % self._uid

    #////////////////////////////
    #// Accessors
    #////////////////////////////

    def type(self):
        """
        @return: A description of this property's type.
        @rtype: L{markup.ParsedDocstring}
        """
        return self._type

    def fget(self):
        """
        @return: The UID of this property's get function.
        @rtype: L{UID}
        """
        return self._fget

    def fset(self):
        """
        @return: The UID of this property's set function.
        @rtype: L{UID}
        """
        return self._fset

    def fdel(self):
        """
        @return: The UID of this property's delete function.
        @rtype: L{UID}
        """
        return self._fdel

##################################################
## Documentation Management
##################################################

class DocMap(UserDict.UserDict):
    """
    A dictionary mapping each object to the object's documentation.
    Typically, modules or classes are added to the C{DocMap} using
    C{add}, which adds an object and everything it contains.  For
    example, the following code constructs a documentation map, adds
    the module "epydoc.epytext" to it, and looks up the documentation
    for "epydoc.epytext.parse":

        >>> docmap = DocMap()               # Construct a docmap
        >>> docmap.add(epydoc.epytext)      # Add epytext to it
        >>> docmap[epydoc.epytext.parse]    # Look up epytext.parse
        <FuncDoc: epydoc.epytext.parse (3 parameters; 1 exceptions)>
    """
    
    def __init__(self, verbosity=0, document_bases=1,
                 document_autogen_vars=1, inheritance_groups=0,
                 inherit_groups=1):
        """
        Create a new empty documentation map.

        @param verbosity: The verbosity of output produced when
            creating documentation for objects.  More positive numbers
            produce more verbose output; negative numbers supress
            warnings and errors.
        @type verbosity: C{int}
        @param document_bases: Whether or not documentation should
            automatically be built for the bases of classes that are
            added to the documentation map.
        @type document_bases: C{boolean}
        @type inheritance_groups: C{boolean}
        @param inheritance_groups: If true, then create a group for
            each base ancestor, containing the members that are
            inherited from that base.  These groups have names of
            the form C{'Inherited from M{base}'}.
        @type inherit_groups: C{boolean}
        @param inherit_groups: If true, then inherit groups from the
            base ancestors.
        """
        self._verbosity = verbosity
        self._document_bases = document_bases
        self._document_autogen_vars = document_autogen_vars
        self._inheritance_groups = inheritance_groups
        self._inherit_groups = inherit_groups
        self.data = {} # UID -> ObjDoc
        self._class_children = {} # UID -> list of UID
        self._package_children = {} # UID -> list of UID
        self._top = None
        self._inherited = 0
        self._sorted_keys = None

    def add_one(self, objID):
        """
        Add an object's documentation to this documentation map.  If
        you also want to include the objects contained by C{obj}, then
        use L{add}.

        @param objID: The UID of the object whose documentation should
            be added to this documentation map.
        @type objID: L{UID}
        @rtype: C{None}
        """
        if objID is None: return
        obj = objID.value()
        self._inherited = 0
        self._top = None
        self._sorted_keys = None
            
        # If we've already documented it, don't do anything.
        if self.data.has_key(objID): return

        # If we couldn't find a UID, don't do anything.
        if objID is None: return

        # If we're being very verbose, then report that we're adding it.
        if self._verbosity > 2:
            if sys.stderr.softspace: print >>sys.stderr
            print >>sys.stderr, '    Building docs for %s' % objID
        
        if objID.is_module():
            self.data[objID] = ModuleDoc(objID, self._verbosity)
            child_modules = self._package_children.get(objID, [])
            if child_modules:
                self.data[objID].add_modules(child_modules)
            packageID = objID.package()
            if packageID is not None:
                if self.data.has_key(packageID):
                    self.data[packageID].add_modules((obj,))
                elif self._package_children.has_key(packageID):
                    self._package_children[packageID].append(obj)
                else:
                    self._package_children[packageID] = [obj]
            if not self._document_autogen_vars:
                self.data[objID].remove_autogenerated_variables()

        elif objID.is_class():
            self.data[objID] = ClassDoc(objID, self._verbosity)
            child_classes = self._class_children.get(objID, [])
            self.data[objID].add_subclasses(child_classes)
            try: bases = obj.__bases__
            except: bases = []
            for base in bases:
                if not (type(base) is types.ClassType or
                        type(base) is _ZopeType or
                        (isinstance(base, types.TypeType))):
                    continue
                baseID=make_uid(base)
                if self.data.has_key(baseID):
                    self.data[baseID].add_subclasses((obj,))
                if self._class_children.has_key(baseID):
                    self._class_children[baseID].append(obj)
                else:
                    self._class_children[baseID] = [obj]

        elif objID.is_function() or objID.is_method():
            self.data[objID] = FuncDoc(objID, self._verbosity)
        elif objID.is_routine():
            self.data[objID] = FuncDoc(objID, self._verbosity)
        elif objID.is_property():
            # Does the class specify a type for the property?
            clsdoc = self.data[objID.parent()]
            typ = clsdoc.property_type(objID)

            self.data[objID] = PropertyDoc(objID, typ, self._verbosity)

    def add(self, obj):
        """
        Add the documentation for an object, and everything contained
        by that object, to this documentation map.

        @param obj: The object whose documentation should be added to
            this documentation map.
        @type obj: any
        @rtype: C{None}
        """
        # Check that it's a good object, and if not, issue a warning.
        if ((type(obj) not in (types.ModuleType, _MethodDescriptorType,
                               types.BuiltinFunctionType, types.MethodType,
                               types.BuiltinMethodType, types.FunctionType,
                               _WrapperDescriptorType, types.ClassType,
                               _ZopeType, _ZopeMethodType,
                               _ZopeCMethodType) and
             not isinstance(obj, types.TypeType))):
            if sys.stderr.softspace: print >>sys.stderr
            estr = 'Error: docmap cannot add an object with type '
            estr += type(obj).__name__
            print >>sys.stderr, estr
            return
        
        objID = make_uid(obj)
        self._add(objID)

    def _add(self, objID):
        if objID is None: return
        if self.data.has_key(objID): return
        
        # Add ourselves.
        self.add_one(objID)
        doc = self.get(objID)
        if not doc: return

        # Recurse to any related objects.
        if objID.is_module():
            for link in doc.functions() + doc.classes():
                self._add(link.target())
        elif objID.is_class():
            for link in doc.allmethods():
                self._add(link.target())
            for var in doc.cvariables():
                if var.uid().is_class():
                    self._add(var.uid())
            for link in doc.properties():
                self.add_one(link.target())

        # Perform inheritance-related linking.
        if objID.is_class():
            # Make sure all bases are added.
            if self._document_bases:
                for base in doc.bases():
                    self._add(base.target())

            doc.inherit([self.data.get(b) for b in doc.base_order()],
                        self._inheritance_groups, self._inherit_groups)

        elif objID.is_any_method():
            # Make sure the class is documented
            self._add(objID.cls())
            classdoc = self.get(objID.cls())
            if classdoc:
                doc.find_override(classdoc.base_order())

    def _toplevel(self, uid):
        """
        @return: True if the object identified by C{uid} is not
            contained (as a sub-package, module contents, class
            contents, etc.) by any other object in this docmap.
        @rtype: C{boolean}
        @param uid: The C{UID} to check.
        @type uid: L{UID}
        """
        for doc in self.values():
            if isinstance(doc, ModuleDoc):
                if uid.is_function():
                    if uid in [l.target() for l in doc.functions()]:
                        return 0
                elif uid.is_class():
                    if uid in [l.target() for l in doc.classes()]:
                        return 0
                if uid in [l.target() for l in doc.variables()]:
                    return 0
            elif isinstance(doc, ClassDoc):
                if uid.is_method():
                    if uid in [l.target() for l in doc.methods()]:
                        return 0
                if uid in [l.target() for l in doc.cvariables()]:
                    return 0
                if uid in [l.target() for l in doc.ivariables()]:
                    return 0
        return 1

    def top(self):
        """
        @return: The list of top-level objects documented by this
            documentation map.  The top-level objects are those that
            are not contained (as sub-packages, module contents, class
            contents, etc) by any other objects in the documentation
            map.
        @rtype: C{list} of L{UID}
        """
        if self._top is None:
            self._top = [uid for uid in self.keys()
                         if self._toplevel(uid)]
        return self._top

    def __getitem__(self, key):
        """
        @return: The documentation for the given object; or the object
            identified by C{key}, if C{key} is a L{UID}.
        @rtype: C{ObjDoc}
        @param key: The object whose documentation should be returned.
        @type key: any
        @raise KeyError: If the documentation for the object
            identified by C{key} is not contained in this
            documentation map.
        """
        if isinstance(key, UID):
            return self.data[key]
        else:
            raise TypeError()

    # RENAME THIS?
    def documented_ancestor(self, method_uid):
        """
        Returns the documentation for the closest ancestor of
        C{method_uid} that defines a docstring.  In particular, search
        through the set of methods that C{method_uid} overrides
        (starting with C{method_uid} itself), and return the first
        method that:
          - defines a docstring
          - matches the signature of C{method_uid}

        If no such method is found, or if C{method_uid} itself is not
        documented by this docmap, then this function returns C{None}.
        
        @type method_uid: L{UID}
        @rtype: L{ObjDoc} or C{None}
        @raise ValueError: If C{method_uid} is not the UID for a method.
        """
        # Check that it's the UID for a method
        if not (method_uid.is_method() or method_uid.is_builtin_method()):
            raise ValueError, '%s is not the UID for a method' % method_uid
        
        # Get the documentation for method_uid
        doc = self.data.get(method_uid)
        if doc is None: return None
        if doc.has_docstring(): return doc

        # Search through the overridden methods until we find a
        # documented ancestor.
        ancestor = doc
        while (not ancestor.has_docstring() and ancestor.matches_override()
               and self.data.has_key(ancestor.overrides())):
            ancestor = self.data[ancestor.overrides()]

        # If we found a documented ancestor, then return it.
        if ancestor.has_docstring(): return ancestor

        # If we didn't, then return None
        else: return None

    def sorted_keys(self):
        if self._sorted_keys is None:
            # Use the decorate-sort-undecorate pattern to make sorting
            # more efficient.
            decorated = [(u.name()=='__init__', u.is_private(),
                          u.name().lower(), u) for u in self.keys()]
            decorated.sort()
            self._sorted_keys = [d[-1] for d in decorated]
        return self._sorted_keys

    def __repr__(self):
        return '<Documentation: '+`len(self.data)`+' objects>'

##################################################
## Helper Functions
##################################################
def _flatten(tree):
    """
    Recursively explore C{tree}, and return an in-order list of all
    leaves.
    
    @return: An in-order list of the leaves of C{tree}.
    @param tree: The tree whose leaves should be returned.  The tree
        structure of C{tree} is represented by tuples and lists.  In
        particular, every tuple or list contained in C{tree} is
        considered a subtree; and any other element is considered a
        leaf. 
    @type tree: C{list} or C{tuple}
    """
    lst = []
    for elt in tree:
        if type(elt) in (types.TupleType, types.ListType):
            lst.extend(_flatten(elt))
        else:
            lst.append(elt)
    return lst

def _getdoc(obj):
    """
    Get the documentation string for an object.  This function is
    similar to L{inspect.getdoc}.  In particular, it finds the minimum
    indentation fromthe second line onwards, and removes that
    indentation from each line.  But it also checks to make sure that
    the docstring is actually a string (since some programs put other
    data in the docstrings).

    @param obj: The object whose documentation string should be returned.
    @type obj: any
    """
    if ((not hasattr(obj, '__doc__')) or
        type(obj.__doc__) is not types.StringType):
        return None
    else:
        return inspect.getdoc(obj)

def _find_docstring(uid):
    """
    @return: The file name and line number of the docstring for the
        given object; or C{None} if the docstring cannot be found.
        Line numbers are indexed from zero (i.e., the first line's
        line number is 0).
    @rtype: C{(string, int)} or C{(None, None)}
    """
    # This function is based on inspect.findsource; but I don't want
    # to use that function directly, because it's not as smart about
    # finding modules for objects (esp. functions).

    # Get the filename of the source file.
    object = uid.value()
    try:
        if uid.is_module(): muid = uid
        else: muid = uid.module()
        filename = muid.value().__file__
    except: return (None, None)
    if filename[-4:-1].lower() == '.py':
        filename = filename[:-1]

    # Read the source file's contents.
    try: lines = open(filename).readlines()
    except: return (None, None)

    # Figure out the starting line number of the object
    linenum = 0
    if inspect.isclass(object):
        pat = re.compile(r'^\s*class\s*%s\b' % object.__name__)
        for linenum in range(len(lines)):
            if pat.match(lines[linenum]): break
        else: return (None, None)
        linenum += 1
    if inspect.ismethod(object): object = object.im_func
    if inspect.isfunction(object): object = object.func_code
    if inspect.istraceback(object): object = object.tb_frame
    if inspect.isframe(object): object = object.f_code
    if inspect.iscode(object):
        if not hasattr(object, 'co_firstlineno'): return (None, None)
        linenum = object.co_firstlineno

    # Find the line number of the docstring.  Assume that it's the
    # first non-blank line after the start of the object, since the
    # docstring has to come first.
    for linenum in range(linenum, len(lines)):
        if lines[linenum].split('#', 1)[0].strip():
            return (filename, linenum)

    # We couldn't find a docstring line number.
    return (None, None)

_IDENTIFIER_LIST_REGEXP = re.compile(r'^[\w.\*]+([\s,:;]\s*[\w.\*]+)*$')
def _descr_to_identifiers(descr):
    """
    Given a C{ParsedDocstring} that contains a list of identifiers,
    return a list of those identifiers.  This is used by fields such
    as C{@group} and C{@sort}, which expect lists of identifiers as
    their values.  To extract the identifiers, the docstring is first
    converted to plaintext, and then split.  The plaintext content of
    the docstring must be a a list of identifiers, separated by
    spaces, commas, colons, or semicolons.

    @rtype: C{list} of C{string}
    @return: A list of the identifier names contained in C{descr}.
    @type descr: L{markup.ParsedDocstring}
    @param descr: A C{ParsedDocstring} containing a list of
        identifiers.
    @raise ValueError: If C{descr} does not contain a valid list of
        identifiers.
    """
    idents = descr.to_plaintext(None).strip()
    idents = re.sub(r'\s+', ' ', idents)
    if not _IDENTIFIER_LIST_REGEXP.match(idents):
        raise ValueError, 'Bad Identifier list: %r' % idents
    rval = re.split('[:;, ] *', idents)
    return rval

def _descr_to_docfield(arg, descr):
    tags = [s.lower() for s in re.split('[:;, ] *', arg)]
    descr = descr.to_plaintext(None).strip()
    args = re.split('[:;,] *', descr)
    if len(args) == 0 or len(args) > 3:
        raise ValueError, 'Wrong number of arguments'
    singular = args[0]
    if len(args) >= 2: plural = args[1]
    else: plural = None
    short = 0
    if len(args) >= 3:
        if args[2] == 'short': short = 1
        else: raise ValueError('Bad arg 2 (expected "short")')
    return DocField(tags, singular, plural, short)

# Copied from http://www.python.org/doc/current/lib/node566.html
def _ast_match(pattern, data, vars=None, indent=0):
    """
    Match C{data} to C{pattern}, with variable extraction.

    The C{pattern} value may contain variables of the form
    C{['M{varname}']} which are allowed to match anything.  The value
    that is matched is returned as part of a dictionary which maps
    'M{varname}' to the matched value.  'M{varname}' is not required
    to be a string object, but using strings makes patterns and the
    code which uses them more readable.

    @return: two values -- a boolean indicating whether a match was
    found and a dictionary mapping variable names to their associated
    values.
    
    @param pattern:
        Pattern to match against, possibly containing variables.

    @param data:
        Data to be checked and against which variables are extracted.

    @param vars:
        Dictionary of variables which have already been found.  If not
        provided, an empty dictionary is created.
    """
    if vars is None:
        vars = {}
    if type(pattern) is types.ListType:  # 'variables' are ['varname']
        vars[pattern[0]] = data
        return 1, vars
    if type(pattern) is not types.TupleType:
        return (pattern == data), vars
    if len(data) != len(pattern):
        return 0, vars
    for pattern, data in map(None, pattern, data):
        same, vars = _ast_match(pattern, data, vars, indent+1)
        if not same:
            break
    return same, vars

def _lookup_class_field(cls, field, base_order=None):
    """
    Find the value for a class's field by looking through its base
    order list, and using getattr to access the first base that
    contains the field.  Note that this is different from just using
    C{getattr} directly, since for methods it will return a method of
    the base class, rather than a method of the given class.  This is
    useful for constructing UIDs for class methods.

    @return: A pair C{(val, container)}, where C{val} is the value
        that was found; and C{container} is the base that contains
        C{val}.
    """
    if base_order is None: base_order = _find_base_order(cls)
    for base in base_order:
        if not hasattr(base, '__dict__'): continue
        if base.__dict__.has_key(field):
            container = make_uid(base)
            try: val = getattr(base, field)
            except: val = base.__getattribute__(base, field)
            break
    else:
        container = make_uid(base_order[0])
        try:
            val = getattr(cls, field)
        except:
            try:
                val = cls.__getattribute__(cls, field)
            except:
                val = '<Not Accessible>'
    return (val, container)

def _find_base_order(cls):
    """
    @return: A list of the base ancestors of C{cls}, in the order that
        they should be searched for attributes.  Each base ancestor
        should be listed exactly once.  The base order list includes
        the given class as its first element.
    @rtype: C{list} of C{class}
    """
    # Try using mro (method resolution operator), if available.
    if hasattr(cls, '__mro__'): return cls.__mro__
    
    # Use new or old inheritance rules?
    new_inheritance = (sys.hexversion >= 0x02020000)

    # Depth-first search,
    base_order = _dfs_bases(cls)

    # Eliminate duplicates.  For old inheritance order, eliminate from
    # front to back; for new inheritance order, eliminate back to front.
    if new_inheritance: base_order.reverse()
    i = 0
    seen_bases = {}
    while i < len(base_order):
        if seen_bases.has_key(base_order[i]):
            del base_order[i]
        else:
            seen_bases[base_order[i]] = 1
            i += 1
    if new_inheritance: base_order.reverse()

    return base_order

def _dfs_bases(cls):
    bases = [cls]
    if not hasattr(cls, '__bases__'): return bases
    for base in cls.__bases__: bases += _dfs_bases(base)
    return bases

