# -*- coding: utf-8 -*-
"""
    abjad.docs.ext.autodoc
    ~~~~~~~~~~~~~~~~~~

    Automatically insert docstrings for functions, classes or whole modules into
    the doctree, thus avoiding duplication between docstrings and documentation
    for those who like elaborate docstrings.

    :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
    :license: BSD, see LICENSE for details.

    Modified for Abjad documentation conventions.
"""

import re
import sys
import inspect
import traceback
from types import FunctionType, BuiltinFunctionType, MethodType

from docutils import nodes
from docutils.utils import assemble_option_dict
from docutils.statemachine import ViewList

from sphinx.util import rpartition, force_decode
from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.application import ExtensionError
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.compat import Directive
#from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \
#     safe_getattr, safe_repr
from sphinx.util.inspect import isdescriptor, safe_getmembers, \
     safe_getattr, safe_repr
from sphinx.util.pycompat import base_exception, class_types
from sphinx.util.docstrings import prepare_docstring

from sphinx.ext.autodoc import Documenter


def getargspec(proc):
    from sphinx.util.inspect import getargspec as sphinx_getargspec
    if proc.__name__ == 'wrapper':
        if proc.__closure__:
            return sphinx_getargspec(proc.__closure__[1].cell_contents)
    return sphinx_getargspec(proc)
    

#: extended signature RE: with explicit module name separated by ::
py_ext_sig_re = re.compile(
    r'''^ ([\w.]+::)?            # explicit module name
          ([\w.]+\.)?            # module and/or class name(s)
          (\w+)  \s*             # thing name
          (?: \((.*)\)           # optional: arguments
           (?:\s* -> \s* (.*))?  #           return annotation
          )? $                   # and nothing more
          ''', re.VERBOSE)


class DefDict(dict):
    """A dict that returns a default on nonexisting keys."""
    def __init__(self, default):
        dict.__init__(self)
        self.default = default
    def __getitem__(self, key):
        try:
            return dict.__getitem__(self, key)
        except KeyError:
            return self.default
    def __nonzero__(self):
        # docutils check "if option_spec"
        return True

identity = lambda x: x


class Options(dict):
    """A dict/attribute hybrid that returns None on nonexisting keys."""
    def __getattr__(self, name):
        try:
            return self[name.replace('_', '-')]
        except KeyError:
            return None


ALL = object()
INSTANCEATTR = object()

def members_option(arg):
    """Used to convert the :members: option to auto directives."""
    if arg is None:
        return ALL
    return [x.strip() for x in arg.split(',')]

def members_set_option(arg):
    """Used to convert the :members: option to auto directives."""
    if arg is None:
        return ALL
    return set(x.strip() for x in arg.split(','))

def bool_option(arg):
    """Used to convert flag options to auto directives.  (Instead of
    directives.flag(), which returns None).
    """
    return True


class AutodocReporter(object):
    """
    A reporter replacement that assigns the correct source name
    and line number to a system message, as recorded in a ViewList.
    """
    def __init__(self, viewlist, reporter):
        self.viewlist = viewlist
        self.reporter = reporter

    def __getattr__(self, name):
        return getattr(self.reporter, name)

    def system_message(self, level, message, *children, **kwargs):
        if 'line' in kwargs and 'source' not in kwargs:
            try:
                source, line = self.viewlist.items[kwargs['line']]
            except IndexError:
                pass
            else:
                kwargs['source'] = source
                kwargs['line'] = line
        return self.reporter.system_message(level, message,
                                            *children, **kwargs)

    def debug(self, *args, **kwargs):
        if self.reporter.debug_flag:
            return self.system_message(0, *args, **kwargs)

    def info(self, *args, **kwargs):
        return self.system_message(1, *args, **kwargs)

    def warning(self, *args, **kwargs):
        return self.system_message(2, *args, **kwargs)

    def error(self, *args, **kwargs):
        return self.system_message(3, *args, **kwargs)

    def severe(self, *args, **kwargs):
        return self.system_message(4, *args, **kwargs)


# Some useful event listener factories for autodoc-process-docstring.

def cut_lines(pre, post=0, what=None):
    """Return a listener that removes the first *pre* and last *post*
    lines of every docstring.  If *what* is a sequence of strings,
    only docstrings of a type in *what* will be processed.

    Use like this (e.g. in the ``setup()`` function of :file:`conf.py`)::

       from sphinx.ext.autodoc import cut_lines
       app.connect('autodoc-process-docstring', cut_lines(4, what=['module']))

    This can (and should) be used in place of :confval:`automodule_skip_lines`.
    """
    def process(app, what_, name, obj, options, lines):
        if what and what_ not in what:
            return
        del lines[:pre]
        if post:
            # remove one trailing blank line.
            if lines and not lines[-1]:
                lines.pop(-1)
            del lines[-post:]
        # make sure there is a blank line at the end
        if lines and lines[-1]:
            lines.append('')
    return process

def between(marker, what=None, keepempty=False, exclude=False):
    """Return a listener that either keeps, or if *exclude* is True excludes,
    lines between lines that match the *marker* regular expression.  If no line
    matches, the resulting docstring would be empty, so no change will be made
    unless *keepempty* is true.

    If *what* is a sequence of strings, only docstrings of a type in *what* will
    be processed.
    """
    marker_re = re.compile(marker)
    def process(app, what_, name, obj, options, lines):
        if what and what_ not in what:
            return
        deleted = 0
        delete = not exclude
        orig_lines = lines[:]
        for i, line in enumerate(orig_lines):
            if delete:
                lines.pop(i - deleted)
                deleted += 1
            if marker_re.match(line):
                delete = not delete
                if delete:
                    lines.pop(i - deleted)
                    deleted += 1
        if not lines and not keepempty:
            lines[:] = orig_lines
        # make sure there is a blank line at the end
        if lines and lines[-1]:
            lines.append('')
    return process


#class Documenter(object):
#    """
#    A Documenter knows how to autodocument a single object type.  When
#    registered with the AutoDirective, it will be used to document objects
#    of that type when needed by autodoc.
#
#    Its *objtype* attribute selects what auto directive it is assigned to
#    (the directive name is 'auto' + objtype), and what directive it generates
#    by default, though that can be overridden by an attribute called
#    *directivetype*.
#
#    A Documenter has an *option_spec* that works like a docutils directive's;
#    in fact, it will be used to parse an auto directive's options that matches
#    the documenter.
#    """
#    #: name by which the directive is called (auto...) and the default
#    #: generated directive name
#    objtype = 'object'
#    #: indentation by which to indent the directive content
#    content_indent = u'   '
#    #: priority if multiple documenters return True from can_document_member
#    priority = 0
#    #: order if autodoc_member_order is set to 'groupwise'
#    member_order = 0
#    #: true if the generated content may contain titles
#    titles_allowed = False
#
#    option_spec = {'noindex': bool_option}
#
#    @staticmethod
#    def get_attr(obj, name, *defargs):
#        """getattr() override for types such as Zope interfaces."""
#        for typ, func in AutoDirective._special_attrgetters.iteritems():
#            if isinstance(obj, typ):
#                return func(obj, name, *defargs)
#        return safe_getattr(obj, name, *defargs)
#
#    @classmethod
#    def can_document_member(cls, member, membername, isattr, parent):
#        """Called to see if a member can be documented by this documenter."""
#        raise NotImplementedError('must be implemented in subclasses')
#
#    def __init__(self, directive, name, indent=u''):
#        self.directive = directive
#        self.env = directive.env
#        self.options = directive.genopt
#        self.name = name
#        self.indent = indent
#        # the module and object path within the module, and the fully
#        # qualified name (all set after resolve_name succeeds)
#        self.modname = None
#        self.module = None
#        self.objpath = None
#        self.fullname = None
#        # extra signature items (arguments and return annotation,
#        # also set after resolve_name succeeds)
#        self.args = None
#        self.retann = None
#        # the object to document (set after import_object succeeds)
#        self.object = None
#        self.object_name = None
#        # the parent/owner of the object to document
#        self.parent = None
#        # the module analyzer to get at attribute docs, or None
#        self.analyzer = None
#
#    def add_line(self, line, source, *lineno):
#        """Append one line of generated reST to the output."""
#        self.directive.result.append(self.indent + line, source, *lineno)
#
#    def resolve_name(self, modname, parents, path, base):
#        """Resolve the module and name of the object to document given by the
#        arguments and the current module/class.
#
#        Must return a pair of the module name and a chain of attributes; for
#        example, it would return ``('zipfile', ['ZipFile', 'open'])`` for the
#        ``zipfile.ZipFile.open`` method.
#        """
#        raise NotImplementedError('must be implemented in subclasses')
#
#    def parse_name(self):
#        """Determine what module to import and what attribute to document.
#
#        Returns True and sets *self.modname*, *self.objpath*, *self.fullname*,
#        *self.args* and *self.retann* if parsing and resolving was successful.
#        """
#        # first, parse the definition -- auto directives for classes and
#        # functions can contain a signature which is then used instead of
#        # an autogenerated one
#        try:
#            explicit_modname, path, base, args, retann = \
#                 py_ext_sig_re.match(self.name).groups()
#        except AttributeError:
#            self.directive.warn('invalid signature for auto%s (%r)' %
#                                (self.objtype, self.name))
#            return False
#
#        # support explicit module and class name separation via ::
#        if explicit_modname is not None:
#            modname = explicit_modname[:-2]
#            parents = path and path.rstrip('.').split('.') or []
#        else:
#            modname = None
#            parents = []
#
#        self.modname, self.objpath = \
#                      self.resolve_name(modname, parents, path, base)
#
#        if not self.modname:
#            return False
#
#        self.args = args
#        self.retann = retann
#        self.fullname = (self.modname or '') + \
#                        (self.objpath and '.' + '.'.join(self.objpath) or '')
#        return True
#
#    def import_object(self):
#        """Import the object given by *self.modname* and *self.objpath* and set
#        it as *self.object*.
#
#        Returns True if successful, False if an error occurred.
#        """
#        try:
#            __import__(self.modname)
#            parent = None
#            obj = self.module = sys.modules[self.modname]
#            for part in self.objpath:
#                parent = obj
#                obj = self.get_attr(obj, part)
#                self.object_name = part
#            self.parent = parent
#            self.object = obj
#            return True
#        # this used to only catch SyntaxError, ImportError and AttributeError,
#        # but importing modules with side effects can raise all kinds of errors
#        except Exception, err:
#            if self.env.app and not self.env.app.quiet:
#                self.env.app.info(traceback.format_exc().rstrip())
#            self.directive.warn(
#                'autodoc can\'t import/find %s %r, it reported error: '
#                '"%s", please check your spelling and sys.path' %
#                (self.objtype, str(self.fullname), err))
#            self.env.note_reread()
#            return False
#
#    def get_real_modname(self):
#        """Get the real module name of an object to document.
#
#        It can differ from the name of the module through which the object was
#        imported.
#        """
#        return self.get_attr(self.object, '__module__', None) or self.modname
#
#    def check_module(self):
#        """Check if *self.object* is really defined in the module given by
#        *self.modname*.
#        """
#        modname = self.get_attr(self.object, '__module__', None)
#        if modname and modname != self.modname:
#            return False
#        return True
#
#    def format_args(self):
#        """Format the argument signature of *self.object*.
#
#        Should return None if the object does not have a signature.
#        """
#        return None
#
#    def format_name(self):
#        """Format the name of *self.object*.
#
#        This normally should be something that can be parsed by the generated
#        directive, but doesn't need to be (Sphinx will display it unparsed
#        then).
#        """
#        # normally the name doesn't contain the module (except for module
#        # directives of course)
#        return '.'.join(self.objpath) or self.modname
#
#    def format_signature(self):
#        """Format the signature (arguments and return annotation) of the object.
#
#        Let the user process it via the ``autodoc-process-signature`` event.
#        """
#        if self.args is not None:
#            # signature given explicitly
#            args = "(%s)" % self.args
#        else:
#            # try to introspect the signature
#            try:
#                args = self.format_args()
#            except Exception, err:
#                self.directive.warn('error while formatting arguments for '
#                                    '%s: %s' % (self.fullname, err))
#                args = None
#
#        retann = self.retann
#
#        result = self.env.app.emit_firstresult(
#            'autodoc-process-signature', self.objtype, self.fullname,
#            self.object, self.options, args, retann)
#        if result:
#            args, retann = result
#
#        if args is not None:
#            return args + (retann and (' -> %s' % retann) or '')
#        else:
#            return ''
#
#    def add_directive_header(self, sig):
#        """Add the directive header and options to the generated content."""
#        domain = getattr(self, 'domain', 'py')
#        directive = getattr(self, 'directivetype', self.objtype)
#        name = self.format_name()
#        self.add_line(u'.. %s:%s:: %s%s' % (domain, directive, name, sig),
#                      '<autodoc>')
#        if self.options.noindex:
#            self.add_line(u'   :noindex:', '<autodoc>')
#        if self.objpath:
#            # Be explicit about the module, this is necessary since .. class::
#            # etc. don't support a prepended module name
#            self.add_line(u'   :module: %s' % self.modname, '<autodoc>')
#
#    def get_doc(self, encoding=None, ignore=1):
#        """Decode and return lines of the docstring(s) for the object."""
#        docstring = self.get_attr(self.object, '__doc__', None)
#        # make sure we have Unicode docstrings, then sanitize and split
#        # into lines
#        if isinstance(docstring, unicode):
#            return [prepare_docstring(docstring, ignore)]
#        elif docstring:
#            return [prepare_docstring(force_decode(docstring, encoding),
#                                      ignore)]
#        return []
#
#    def process_doc(self, docstrings):
#        """Let the user process the docstrings before adding them."""
#        for docstringlines in docstrings:
#            if self.env.app:
#                # let extensions preprocess docstrings
#                self.env.app.emit('autodoc-process-docstring',
#                                  self.objtype, self.fullname, self.object,
#                                  self.options, docstringlines)
#            for line in docstringlines:
#                yield line
#
#    def add_content(self, more_content, no_docstring=False):
#        """Add content from docstrings, attribute documentation and user."""
#        # set sourcename and add content from attribute documentation
#        if self.analyzer:
#            # prevent encoding errors when the file name is non-ASCII
#            if not isinstance(self.analyzer.srcname, unicode):
#                filename = unicode(self.analyzer.srcname,
#                                   sys.getfilesystemencoding(), 'replace')
#            else:
#                filename = self.analyzer.srcname
#            sourcename = u'%s:docstring of %s' % (filename, self.fullname)
#
#            attr_docs = self.analyzer.find_attr_docs()
#            if self.objpath:
#                key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
#                if key in attr_docs:
#                    no_docstring = True
#                    docstrings = [attr_docs[key]]
#                    for i, line in enumerate(self.process_doc(docstrings)):
#                        self.add_line(line, sourcename, i)
#        else:
#            sourcename = u'docstring of %s' % self.fullname
#
#        # add content from docstrings
#        if not no_docstring:
#            encoding = self.analyzer and self.analyzer.encoding
#            docstrings = self.get_doc(encoding)
#            if not docstrings:
#                # append at least a dummy docstring, so that the event
#                # autodoc-process-docstring is fired and can add some
#                # content if desired
#                docstrings.append([])
#            for i, line in enumerate(self.process_doc(docstrings)):
#                self.add_line(line, sourcename, i)
#
#        # add additional content (e.g. from document), if present
#        if more_content:
#            for line, src in zip(more_content.data, more_content.items):
#                self.add_line(line, src[0], src[1])
#
#    def get_object_members(self, want_all):
#        """Return `(members_check_module, members)` where `members` is a
#        list of `(membername, member)` pairs of the members of *self.object*.
#
#        If *want_all* is True, return all members.  Else, only return those
#        members given by *self.options.members* (which may also be none).
#        """
#        if not want_all:
#            if not self.options.members:
#                return False, []
#            # specific members given
#            ret = []
#            for mname in self.options.members:
#                try:
#                    ret.append((mname, self.get_attr(self.object, mname)))
#                except AttributeError:
#                    self.directive.warn('missing attribute %s in object %s'
#                                        % (mname, self.fullname))
#            return False, ret
#
#        if self.options.inherited_members:
#            # safe_getmembers() uses dir() which pulls in members from all
#            # base classes
#            members = safe_getmembers(self.object)
#        else:
#            # __dict__ contains only the members directly defined in
#            # the class (but get them via getattr anyway, to e.g. get
#            # unbound method objects instead of function objects);
#            # using keys() because apparently there are objects for which
#            # __dict__ changes while getting attributes
#            obj_dict = self.get_attr(self.object, '__dict__')
#            members = [(mname, self.get_attr(self.object, mname, None))
#                       for mname in obj_dict.keys()]
#        membernames = set(m[0] for m in members)
#        # add instance attributes from the analyzer
#        if self.analyzer:
#            attr_docs = self.analyzer.find_attr_docs()
#            namespace = '.'.join(self.objpath)
#            for item in attr_docs.iteritems():
#                if item[0][0] == namespace:
#                    if item[0][1] not in membernames:
#                        members.append((item[0][1], INSTANCEATTR))
#        return False, sorted(members)
#
#    def filter_members(self, members, want_all):
#        """Filter the given member list.
#
#        Members are skipped if
#
#        - they are private (except if given explicitly or the private-members
#          option is set)
#        - they are special methods (except if given explicitly or the
#          special-members option is set)
#        - they are undocumented (except if the undoc-members option is set)
#
#        The user can override the skipping decision by connecting to the
#        ``autodoc-skip-member`` event.
#        """
#        ret = []
#
#        # search for members in source code too
#        namespace = '.'.join(self.objpath)  # will be empty for modules
#
#        if self.analyzer:
#            attr_docs = self.analyzer.find_attr_docs()
#        else:
#            attr_docs = {}
#
#        # process members and determine which to skip
#        for (membername, member) in members:
#            # if isattr is True, the member is documented as an attribute
#            isattr = False
#
#            doc = self.get_attr(member, '__doc__', None)
#            # if the member __doc__ is the same as self's __doc__, it's just
#            # inherited and therefore not the member's doc
#            cls = self.get_attr(member, '__class__', None)
#            if cls:
#                cls_doc = self.get_attr(cls, '__doc__', None)
#                if cls_doc == doc:
#                    doc = None
#            has_doc = bool(doc)
#
#            keep = False
#            if want_all and membername.startswith('__') and \
#                   membername.endswith('__') and len(membername) > 4:
#                # special __methods__
#                if self.options.special_members and membername != '__doc__':
#                    keep = has_doc or self.options.undoc_members
#            elif want_all and membername.startswith('_'):
#                # ignore members whose name starts with _ by default
#                keep = self.options.private_members and \
#                       (has_doc or self.options.undoc_members)
#            elif (namespace, membername) in attr_docs:
#                # keep documented attributes
#                keep = True
#                isattr = True
#            else:
#                # ignore undocumented members if :undoc-members: is not given
#                keep = has_doc or self.options.undoc_members
#
#            # give the user a chance to decide whether this member
#            # should be skipped
#            if self.env.app:
#                # let extensions preprocess docstrings
#                skip_user = self.env.app.emit_firstresult(
#                    'autodoc-skip-member', self.objtype, membername, member,
#                    not keep, self.options)
#                if skip_user is not None:
#                    keep = not skip_user
#
#            if keep:
#                ret.append((membername, member, isattr))
#
#        return ret
#
#    def document_members(self, all_members=False):
#        """Generate reST for member documentation.
#
#        If *all_members* is True, do all members, else those given by
#        *self.options.members*.
#        """
#        # set current namespace for finding members
#        self.env.temp_data['autodoc:module'] = self.modname
#        if self.objpath:
#            self.env.temp_data['autodoc:class'] = self.objpath[0]
#
#        want_all = all_members or self.options.inherited_members or \
#                   self.options.members is ALL
#        # find out which members are documentable
#        members_check_module, members = self.get_object_members(want_all)
#
#        # remove members given by exclude-members
#        if self.options.exclude_members:
#            members = [(membername, member) for (membername, member) in members
#                       if membername not in self.options.exclude_members]
#
#        # document non-skipped members
#        memberdocumenters = []
#        for (mname, member, isattr) in self.filter_members(members, want_all):
#            classes = [cls for cls in AutoDirective._registry.itervalues()
#                       if cls.can_document_member(member, mname, isattr, self)]
#            if not classes:
#                # don't know how to document this member
#                continue
#            # prefer the documenter with the highest priority
#            classes.sort(key=lambda cls: cls.priority)
#            # give explicitly separated module name, so that members
#            # of inner classes can be documented
#            full_mname = self.modname + '::' + \
#                              '.'.join(self.objpath + [mname])
#            documenter = classes[-1](self.directive, full_mname, self.indent)
#            memberdocumenters.append((documenter, isattr))
#        member_order = self.options.member_order or \
#                       self.env.config.autodoc_member_order
#        if member_order == 'groupwise':
#            # sort by group; relies on stable sort to keep items in the
#            # same group sorted alphabetically
#            memberdocumenters.sort(key=lambda e: e[0].member_order)
#        elif member_order == 'bysource' and self.analyzer:
#            # sort by source order, by virtue of the module analyzer
#            tagorder = self.analyzer.tagorder
#            def keyfunc(entry):
#                fullname = entry[0].name.split('::')[1]
#                return tagorder.get(fullname, len(tagorder))
#            memberdocumenters.sort(key=keyfunc)
#
#        for documenter, isattr in memberdocumenters:
#            documenter.generate(
#                all_members=True, real_modname=self.real_modname,
#                check_module=members_check_module and not isattr)
#
#        # reset current objects
#        self.env.temp_data['autodoc:module'] = None
#        self.env.temp_data['autodoc:class'] = None
#
#    def generate(self, more_content=None, real_modname=None,
#                 check_module=False, all_members=False):
#        """Generate reST for the object given by *self.name*, and possibly for
#        its members.
#
#        If *more_content* is given, include that content. If *real_modname* is
#        given, use that module name to find attribute docs. If *check_module* is
#        True, only generate if the object is defined in the module name it is
#        imported from. If *all_members* is True, document all members.
#        """
#        if not self.parse_name():
#            # need a module to import
#            self.directive.warn(
#                'don\'t know which module to import for autodocumenting '
#                '%r (try placing a "module" or "currentmodule" directive '
#                'in the document, or giving an explicit module name)'
#                % self.name)
#            return
#
#        # now, import the module and get object to document
#        if not self.import_object():
#            return
#
#        # If there is no real module defined, figure out which to use.
#        # The real module is used in the module analyzer to look up the module
#        # where the attribute documentation would actually be found in.
#        # This is used for situations where you have a module that collects the
#        # functions and classes of internal submodules.
#        self.real_modname = real_modname or self.get_real_modname()
#
#        # try to also get a source code analyzer for attribute docs
#        try:
#            self.analyzer = ModuleAnalyzer.for_module(self.real_modname)
#            # parse right now, to get PycodeErrors on parsing (results will
#            # be cached anyway)
#            self.analyzer.find_attr_docs()
#        except PycodeError:
#            # no source file -- e.g. for builtin and C modules
#            self.analyzer = None
#            # at least add the module.__file__ as a dependency
#            if hasattr(self.module, '__file__') and self.module.__file__:
#                self.directive.filename_set.add(self.module.__file__)
#        else:
#            self.directive.filename_set.add(self.analyzer.srcname)
#
#        # check __module__ of object (for members not given explicitly)
#        if check_module:
#            if not self.check_module():
#                return
#
#        # make sure that the result starts with an empty line.  This is
#        # necessary for some situations where another directive preprocesses
#        # reST and no starting newline is present
#        self.add_line(u'', '<autodoc>')
#
#        # format the object's signature, if any
#        sig = self.format_signature()
#
#        # generate the directive header and options, if applicable
#        self.add_directive_header(sig)
#        self.add_line(u'', '<autodoc>')
#
#        # e.g. the module directive doesn't have content
#        self.indent += self.content_indent
#
#        # add all content (from docstrings, attribute docs etc.)
#        self.add_content(more_content)
#
#        # document members, if possible
#        self.document_members(all_members)


class ModuleDocumenter(Documenter):
    """
    Specialized Documenter subclass for modules.
    """
    objtype = 'module'
    content_indent = u''
    titles_allowed = True

    option_spec = {
        'members': members_option, 'undoc-members': bool_option,
        'noindex': bool_option, 'inherited-members': bool_option,
        'show-inheritance': bool_option, 'synopsis': identity,
        'platform': identity, 'deprecated': bool_option,
        'member-order': identity, 'exclude-members': members_set_option,
        'private-members': bool_option, 'special-members': bool_option,
    }

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        # don't document submodules automatically
        return False

    def resolve_name(self, modname, parents, path, base):
        if modname is not None:
            self.directive.warn('"::" in automodule name doesn\'t make sense')
        return (path or '') + base, []

    def parse_name(self):
        ret = Documenter.parse_name(self)
        if self.args or self.retann:
            self.directive.warn('signature arguments or return annotation '
                                'given for automodule %s' % self.fullname)
        return ret

    def add_directive_header(self, sig):
        Documenter.add_directive_header(self, sig)

        # add some module-specific options
        if self.options.synopsis:
            self.add_line(
                u'   :synopsis: ' + self.options.synopsis, '<autodoc>')
        if self.options.platform:
            self.add_line(
                u'   :platform: ' + self.options.platform, '<autodoc>')
        if self.options.deprecated:
            self.add_line(u'   :deprecated:', '<autodoc>')

    def get_object_members(self, want_all):
        if want_all:
            if not hasattr(self.object, '__all__'):
                # for implicit module members, check __module__ to avoid
                # documenting imported objects
                return True, safe_getmembers(self.object)
            else:
                memberlist = self.object.__all__
        else:
            memberlist = self.options.members or []
        ret = []
        for mname in memberlist:
            try:
                ret.append((mname, safe_getattr(self.object, mname)))
            except AttributeError:
                self.directive.warn(
                    'missing attribute mentioned in :members: or __all__: '
                    'module %s, attribute %s' % (
                    safe_getattr(self.object, '__name__', '???'), mname))
        return False, ret


class ModuleLevelDocumenter(Documenter):
    """
    Specialized Documenter subclass for objects on module level (functions,
    classes, data/constants).
    """
    def resolve_name(self, modname, parents, path, base):
        if modname is None:
            if path:
                modname = path.rstrip('.')
            else:
                # if documenting a toplevel object without explicit module,
                # it can be contained in another auto directive ...
                modname = self.env.temp_data.get('autodoc:module')
                # ... or in the scope of a module directive
                if not modname:
                    modname = self.env.temp_data.get('py:module')
                # ... else, it stays None, which means invalid
        return modname, parents + [base]


class ClassLevelDocumenter(Documenter):
    """
    Specialized Documenter subclass for objects on class level (methods,
    attributes).
    """
    def resolve_name(self, modname, parents, path, base):
        if modname is None:
            if path:
                mod_cls = path.rstrip('.')
            else:
                mod_cls = None
                # if documenting a class-level object without path,
                # there must be a current class, either from a parent
                # auto directive ...
                mod_cls = self.env.temp_data.get('autodoc:class')
                # ... or from a class directive
                if mod_cls is None:
                    mod_cls = self.env.temp_data.get('py:class')
                # ... if still None, there's no way to know
                if mod_cls is None:
                    return None, []
            modname, cls = rpartition(mod_cls, '.')
            parents = [cls]
            # if the module name is still missing, get it like above
            if not modname:
                modname = self.env.temp_data.get('autodoc:module')
            if not modname:
                modname = self.env.temp_data.get('py:module')
            # ... else, it stays None, which means invalid
        return modname, parents + [base]


class DocstringSignatureMixin(object):
    """
    Mixin for FunctionDocumenter and MethodDocumenter to provide the
    feature of reading the signature from the docstring.
    """

    def _find_signature(self, encoding=None):
        docstrings = Documenter.get_doc(self, encoding, 2)
        if len(docstrings) != 1:
            return
        doclines = docstrings[0]
        setattr(self, '__new_doclines', doclines)
        if not doclines:
            return
        # match first line of docstring against signature RE
        match = py_ext_sig_re.match(doclines[0])
        if not match:
            return
        exmod, path, base, args, retann = match.groups()
        # the base name must match ours
        if not self.objpath or base != self.objpath[-1]:
            return
        # ok, now jump over remaining empty lines and set the remaining
        # lines as the new doclines
        i = 1
        while i < len(doclines) and not doclines[i].strip():
            i += 1
        setattr(self, '__new_doclines', doclines[i:])
        return args, retann

    def get_doc(self, encoding=None, ignore=1):
        lines = getattr(self, '__new_doclines', None)
        if lines is not None:
            return [lines]
        return Documenter.get_doc(self, encoding, ignore)

    def format_signature(self):
        if self.args is None and self.env.config.autodoc_docstring_signature:
            # only act if a signature is not explicitly given already, and if
            # the feature is enabled
            result = self._find_signature()
            if result is not None:
                self.args, self.retann = result
        return Documenter.format_signature(self)


class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter):
    """
    Specialized Documenter subclass for functions.
    """
    objtype = 'function'
    member_order = 30

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        return isinstance(member, (FunctionType, BuiltinFunctionType))

    def format_args(self):
        if inspect.isbuiltin(self.object) or \
               inspect.ismethoddescriptor(self.object):
            # cannot introspect arguments of a C function or method
            return None
        try:
            argspec = getargspec(self.object)
        except TypeError:
            # if a class should be documented as function (yay duck
            # typing) we try to use the constructor signature as function
            # signature without the first argument.
            try:
                argspec = getargspec(self.object.__new__)
            except TypeError:
                argspec = getargspec(self.object.__init__)
                if argspec[0]:
                    del argspec[0][0]
        args = inspect.formatargspec(*argspec)
        # escape backslashes for reST
        args = args.replace('\\', '\\\\')
        return args

    def document_members(self, all_members=False):
        pass

    def format_name(self):
        """Format the name of *self.object*.

        This normally should be something that can be parsed by the generated
        directive, but doesn't need to be (Sphinx will display it unparsed
        then).

        Modified for Abjad documentation conventions.
        """
        # normally the name doesn't contain the module (except for module
        # directives of course)
        pieces = self.modname.split('.')
        #return '{}.{}'.format(pieces[-2], pieces[-1])
        modname = pieces[-2:]
        return '.'.join(modname)


class ClassDocumenter(ModuleLevelDocumenter):
    """
    Specialized Documenter subclass for classes.
    """
    objtype = 'class'
    member_order = 20
    option_spec = {
        'members': members_option, 'undoc-members': bool_option,
        'noindex': bool_option, 'inherited-members': bool_option,
        'show-inheritance': bool_option, 'member-order': identity,
        'exclude-members': members_set_option,
        'private-members': bool_option, 'special-members': bool_option,
    }

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        return isinstance(member, class_types)

    def import_object(self):
        ret = ModuleLevelDocumenter.import_object(self)
        # if the class is documented under another name, document it
        # as data/attribute
        if ret:
            if hasattr(self.object, '__name__'):
                self.doc_as_attr = (self.objpath[-1] != self.object.__name__)
            else:
                self.doc_as_attr = True
        return ret

    def format_args(self):
        # for classes, the relevant signature is the __init__ method's
        initmeth = self.get_attr(self.object, '__init__', None)
        # classes without __init__ method, default __init__ or
        # __init__ written in C?
        if initmeth is None or initmeth is object.__init__ or not \
               (inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
            return None
        try:
            argspec = getargspec(initmeth)
        except TypeError:
            # still not possible: happens e.g. for old-style classes
            # with __init__ in C
            return None
        if argspec[0] and argspec[0][0] in ('cls', 'self'):
            del argspec[0][0]
        return inspect.formatargspec(*argspec)

    def format_name(self):
        """Format the name of *self.object*.

        This normally should be something that can be parsed by the generated
        directive, but doesn't need to be (Sphinx will display it unparsed
        then).

        Modified for Abjad documentation conventions.
        """
        # normally the name doesn't contain the module (except for module
        # directives of course)
        pieces = self.modname.split('.')
        return '{}.{}'.format(pieces[-3], pieces[-1])
        
    def format_signature(self):
        if self.doc_as_attr:
            return ''
        return ModuleLevelDocumenter.format_signature(self)

    def add_directive_header(self, sig):
        if self.doc_as_attr:
            self.directivetype = 'attribute'
        Documenter.add_directive_header(self, sig)

        # add inheritance info, if wanted
        if not self.doc_as_attr and self.options.show_inheritance:
            self.add_line(u'', '<autodoc>')
            if len(self.object.__bases__):
                bases = [b.__module__ == '__builtin__' and
                         u':class:`%s`' % b.__name__ or
                         u':class:`%s.%s`' % (b.__module__, b.__name__)
                         for b in self.object.__bases__]
                self.add_line(_(u'   Bases: %s') % ', '.join(bases),
                              '<autodoc>')

    def get_doc(self, encoding=None, ignore=1):
        content = self.env.config.autoclass_content

        docstrings = []
        attrdocstring = self.get_attr(self.object, '__doc__', None)
        if attrdocstring:
            docstrings.append(attrdocstring)

        # for classes, what the "docstring" is can be controlled via a
        # config value; the default is only the class docstring
        if content in ('both', 'init'):
            initdocstring = self.get_attr(
                self.get_attr(self.object, '__init__', None), '__doc__')
            # for new-style classes, no __init__ means default __init__
            if initdocstring == object.__init__.__doc__:
                initdocstring = None
            if initdocstring:
                if content == 'init':
                    docstrings = [initdocstring]
                else:
                    docstrings.append(initdocstring)
        doc = []
        for docstring in docstrings:
            if not isinstance(docstring, unicode):
                docstring = force_decode(docstring, encoding)
            doc.append(prepare_docstring(docstring))
        return doc

    def add_content(self, more_content, no_docstring=False):
        if self.doc_as_attr:
            classname = safe_getattr(self.object, '__name__', None)
            if classname:
                content = ViewList(
                    [_('alias of :class:`%s`') % classname], source='')
                ModuleLevelDocumenter.add_content(self, content,
                                                  no_docstring=True)
        else:
            ModuleLevelDocumenter.add_content(self, more_content)

    def document_members(self, all_members=False):
        if self.doc_as_attr:
            return
        ModuleLevelDocumenter.document_members(self, all_members)


class ExceptionDocumenter(ClassDocumenter):
    """
    Specialized ClassDocumenter subclass for exceptions.
    """
    objtype = 'exception'
    member_order = 10

    # needs a higher priority than ClassDocumenter
    priority = 10

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        return isinstance(member, class_types) and \
               issubclass(member, base_exception)


class DataDocumenter(ModuleLevelDocumenter):
    """
    Specialized Documenter subclass for data items.
    """
    objtype = 'data'
    member_order = 40
    priority = -10

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        return isinstance(parent, ModuleDocumenter) and isattr

    def add_directive_header(self, sig):
        ModuleLevelDocumenter.add_directive_header(self, sig)
        try:
            objrepr = safe_repr(self.object)
        except ValueError:
            pass
        else:
            self.add_line(u'   :annotation: = ' + objrepr, '<autodoc>')

    def document_members(self, all_members=False):
        pass


class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter):
    """
    Specialized Documenter subclass for methods (normal, static and class).
    """
    objtype = 'method'
    member_order = 50
    priority = 0

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        return inspect.isroutine(member) and \
               not isinstance(parent, ModuleDocumenter)

    if sys.version_info >= (3, 0):
        def import_object(self):
            ret = ClassLevelDocumenter.import_object(self)
            obj_from_parent = self.parent.__dict__.get(self.object_name)
            if isinstance(obj_from_parent, classmethod):
                self.directivetype = 'classmethod'
                self.member_order = self.member_order - 1
            elif isinstance(obj_from_parent, staticmethod):
                self.directivetype = 'staticmethod'
                self.member_order = self.member_order - 1
            else:
                self.directivetype = 'method'
            return ret
    else:
        def import_object(self):
            ret = ClassLevelDocumenter.import_object(self)
            if isinstance(self.object, classmethod) or \
                   (isinstance(self.object, MethodType) and
                    self.object.im_self is not None):
                self.directivetype = 'classmethod'
                # document class and static members before ordinary ones
                self.member_order = self.member_order - 1
            elif isinstance(self.object, FunctionType) or \
                 (isinstance(self.object, BuiltinFunctionType) and
                  hasattr(self.object, '__self__') and
                  self.object.__self__ is not None):
                self.directivetype = 'staticmethod'
                # document class and static members before ordinary ones
                self.member_order = self.member_order - 1
            else:
                self.directivetype = 'method'
            return ret

    def format_args(self):
        if inspect.isbuiltin(self.object) or \
               inspect.ismethoddescriptor(self.object):
            # can never get arguments of a C function or method
            return None
        argspec = getargspec(self.object)
        if argspec[0] and argspec[0][0] in ('cls', 'self'):
            del argspec[0][0]
        return inspect.formatargspec(*argspec)

    def document_members(self, all_members=False):
        pass


class AttributeDocumenter(ClassLevelDocumenter):
    """
    Specialized Documenter subclass for attributes.
    """
    objtype = 'attribute'
    member_order = 60

    # must be higher than the MethodDocumenter, else it will recognize
    # some non-data descriptors as methods
    priority = 10

    method_types = (FunctionType, BuiltinFunctionType, MethodType)

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        isdatadesc = isdescriptor(member) and not \
                     isinstance(member, cls.method_types) and not \
                     type(member).__name__ == "method_descriptor"
        return isdatadesc or (not isinstance(parent, ModuleDocumenter)
                              and not inspect.isroutine(member)
                              and not isinstance(member, class_types))

    def document_members(self, all_members=False):
        pass

    def import_object(self):
        ret = ClassLevelDocumenter.import_object(self)
        if isdescriptor(self.object) and \
               not isinstance(self.object, self.method_types):
            self._datadescriptor = True
        else:
            # if it's not a data descriptor
            self._datadescriptor = False
        return ret

    def get_real_modname(self):
        return self.get_attr(self.parent or self.object, '__module__', None) \
               or self.modname

    def add_directive_header(self, sig):
        ClassLevelDocumenter.add_directive_header(self, sig)
        if not self._datadescriptor:
            try:
                objrepr = safe_repr(self.object)
            except ValueError:
                pass
            else:
                self.add_line(u'   :annotation: = ' + objrepr, '<autodoc>')

    def add_content(self, more_content, no_docstring=False):
        if not self._datadescriptor:
            # if it's not a data descriptor, its docstring is very probably the
            # wrong thing to display
            no_docstring = True
        ClassLevelDocumenter.add_content(self, more_content, no_docstring)


class InstanceAttributeDocumenter(AttributeDocumenter):
    """
    Specialized Documenter subclass for attributes that cannot be imported
    because they are instance attributes (e.g. assigned in __init__).
    """
    objtype = 'instanceattribute'
    directivetype = 'attribute'
    member_order = 60

    # must be higher than AttributeDocumenter
    priority = 11

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        """This documents only INSTANCEATTR members."""
        return isattr and (member is INSTANCEATTR)

    def import_object(self):
        """Never import anything."""
        # disguise as an attribute
        self.objtype = 'attribute'
        self._datadescriptor = False
        return True

    def add_content(self, more_content, no_docstring=False):
        """Never try to get a docstring from the object."""
        AttributeDocumenter.add_content(self, more_content, no_docstring=True)


class AutoDirective(Directive):
    """
    The AutoDirective class is used for all autodoc directives.  It dispatches
    most of the work to one of the Documenters, which it selects through its
    *_registry* dictionary.

    The *_special_attrgetters* attribute is used to customize ``getattr()``
    calls that the Documenters make; its entries are of the form ``type:
    getattr_function``.

    Note: When importing an object, all items along the import chain are
    accessed using the descendant's *_special_attrgetters*, thus this
    dictionary should include all necessary functions for accessing
    attributes of the parents.
    """
    # a registry of objtype -> documenter class
    _registry = {}

    # a registry of type -> getattr function
    _special_attrgetters = {}

    # flags that can be given in autodoc_default_flags
    _default_flags = set([
        'members', 'undoc-members', 'inherited-members', 'show-inheritance',
        'private-members', 'special-members',
    ])

    # standard docutils directive settings
    has_content = True
    required_arguments = 1
    optional_arguments = 0
    final_argument_whitespace = True
    # allow any options to be passed; the options are parsed further
    # by the selected Documenter
    option_spec = DefDict(identity)

    def warn(self, msg):
        self.warnings.append(self.reporter.warning(msg, line=self.lineno))

    def run(self):
        self.filename_set = set()  # a set of dependent filenames
        self.reporter = self.state.document.reporter
        self.env = self.state.document.settings.env
        self.warnings = []
        self.result = ViewList()

        # find out what documenter to call
        objtype = self.name[4:]
        doc_class = self._registry[objtype]
        # add default flags
        for flag in self._default_flags:
            if flag not in doc_class.option_spec:
                continue
            negated = self.options.pop('no-' + flag, 'not given') is None
            if flag in self.env.config.autodoc_default_flags and \
               not negated:
                self.options[flag] = None
        # process the options with the selected documenter's option_spec
        self.genopt = Options(assemble_option_dict(
            self.options.items(), doc_class.option_spec))
        # generate the output
        documenter = doc_class(self, self.arguments[0])
        documenter.generate(more_content=self.content)
        if not self.result:
            return self.warnings

        # record all filenames as dependencies -- this will at least
        # partially make automatic invalidation possible
        for fn in self.filename_set:
            self.state.document.settings.record_dependencies.add(fn)

        # use a custom reporter that correctly assigns lines to source
        # filename/description and lineno
        old_reporter = self.state.memo.reporter
        self.state.memo.reporter = AutodocReporter(self.result,
                                                   self.state.memo.reporter)

        if documenter.titles_allowed:
            node = nodes.section()
            # necessary so that the child nodes get the right source/line set
            node.document = self.state.document
            nested_parse_with_titles(self.state, self.result, node)
        else:
            node = nodes.paragraph()
            node.document = self.state.document
            self.state.nested_parse(self.result, 0, node)
        self.state.memo.reporter = old_reporter
        return self.warnings + node.children


#def add_documenter(cls):
#    """Register a new Documenter."""
#    print 'LOCAL COPY'
#    if not issubclass(cls, Documenter):
#        raise ExtensionError('autodoc documenter %r must be a subclass '
#                             'of Documenter' % cls)
#    # actually, it should be possible to override Documenters
#    #if cls.objtype in AutoDirective._registry:
#    #    raise ExtensionError('autodoc documenter for %r is already '
#    #                         'registered' % cls.objtype)
#    AutoDirective._registry[cls.objtype] = cls


def setup(app):
    app.add_autodocumenter(ModuleDocumenter)
    app.add_autodocumenter(ClassDocumenter)
    app.add_autodocumenter(ExceptionDocumenter)
    app.add_autodocumenter(DataDocumenter)
    app.add_autodocumenter(FunctionDocumenter)
    app.add_autodocumenter(MethodDocumenter)
    app.add_autodocumenter(AttributeDocumenter)
    app.add_autodocumenter(InstanceAttributeDocumenter)

    app.add_config_value('autoclass_content', 'class', True)
    app.add_config_value('autodoc_member_order', 'alphabetic', True)
    app.add_config_value('autodoc_default_flags', [], True)
    app.add_config_value('autodoc_docstring_signature', True, True)
    app.add_event('autodoc-process-docstring')
    app.add_event('autodoc-process-signature')
    app.add_event('autodoc-skip-member')


class testcls:
    """test doc string"""

    def __getattr__(self, x):
        return x

    def __setattr__(self, x, y):
        """Attr setter."""
