<gh_stars>0
#----------------------------------------------------------------------------
#
#  Copyright (c) 2014, Enthought, Inc.
#  All rights reserved.
#
#  This software is provided without warranty under the terms of the BSD
#  license included in /LICENSE.txt and may be redistributed only
#  under the conditions described in the aforementioned license.  The license
#  is also available online at http://www.enthought.com/licenses/BSD.txt
#
#  Thanks for using Enthought open source!
#
#----------------------------------------------------------------------------
from __future__ import unicode_literals

import ast
import traceback
import sys
import inspect
from _ast import ClassDef, Assign

from sphinx.ext.autodoc import ClassLevelDocumenter

from traits.has_traits import MetaHasTraits
from traits.trait_handlers import TraitType


def is_class_trait(name, cls):
    """ Check if the name is in the list of class defined traits of ``cls``.
    """
    return isinstance(cls, MetaHasTraits) and name in cls.__class_traits__


class TraitDocumenter(ClassLevelDocumenter):
    """ Specialized Documenter subclass for trait attributes.

    The class defines a new documenter that recovers the trait definition
    signature of module level and class level traits.

    To use the documenter, append the module path in the extension
    attribute of the `conf.py`.

    .. warning::

        Using the TraitDocumenter in conjunction with TraitsDoc is not
        advised.

    """

    objtype = 'traitattribute'
    directivetype = 'attribute'
    member_order = 60

    # must be higher than other attribute documenters
    priority = 12

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        """ Check that the documented member is a trait instance.
        """
        return (
            isattr and
            issubclass(type(member), TraitType) or
            is_class_trait(membername, parent.object))

    def document_members(self, all_members=False):
        # Trait attributes have no members """
        pass

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

    def import_object(self):
        """ Get the Trait object.

        Notes
        -----
        Code adapted from autodoc.Documenter.import_object.

        """
        try:
            __import__(self.modname)
            current = self.module = sys.modules[self.modname]
            for part in self.objpath[:-1]:
                current = self.get_attr(current, part)
            name = self.objpath[-1]
            self.object_name = name
            self.object = None
            self.parent = current
            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 as err:
            if self.env.app and not self.env.app.quiet:
                self.env.app.info(traceback.format_exc().rstrip())
            msg = (
                'autodoc can\'t import/find {0} {r1}, it reported error: '
                '"{2}", please check your spelling and sys.path')
            self.directive.warn(msg.format(
                self.objtype, str(self.fullname), err))
            self.env.note_reread()
            return False

    def add_directive_header(self, sig):
        """ Add the sphinx directives.

        Add the 'attribute' directive with the annotation option
        set to the trait definition.

        """
        ClassLevelDocumenter.add_directive_header(self, sig)
        definition = self.get_trait_definition()
        self.add_line(
            '   :annotation: = {0}'.format(definition), '<autodoc>')

    def get_trait_definition(self):
        """ Retrieve the Trait attribute definition
        """
        # Get the class source and tokenize it.
        source = inspect.getsource(self.parent)

        nodes = ast.parse(source)
        for node in ast.iter_child_nodes(nodes):
            if isinstance(node, ClassDef):
                parent_node = node
                break
        else:
            return ''

        for node in ast.iter_child_nodes(parent_node):
            if isinstance(node, Assign):
                name = node.targets[0]
                if name.id == self.object_name:
                    break
        else:
            return ''

        endlineno = name.lineno
        for item in ast.walk(node):
            if hasattr(item, 'lineno'):
                endlineno = max(endlineno, item.lineno)

        definition_lines = [
            line.strip()
            for line in source.splitlines()[name.lineno-1:endlineno]]
        definition = ''.join(definition_lines)
        equal = definition.index('=')
        return definition[equal + 1:].lstrip()

#----------------------------------------------------------------------------
#
#  Copyright (c) 2014, Enthought, Inc.
#  All rights reserved.
#
#  This software is provided without warranty under the terms of the BSD
#  license included in /LICENSE.txt and may be redistributed only
#  under the conditions described in the aforementioned license.  The license
#  is also available online at http://www.enthought.com/licenses/BSD.txt
#
#  Thanks for using Enthought open source!
#
#----------------------------------------------------------------------------
from __future__ import unicode_literals

import ast
import traceback
import sys
import inspect
from _ast import ClassDef, Assign

from sphinx.ext.autodoc import ClassLevelDocumenter

from traits.has_traits import MetaHasTraits
from traits.trait_handlers import TraitType


def is_class_trait(name, cls):
    """ Check if the name is in the list of class defined traits of ``cls``.
    """
    return isinstance(cls, MetaHasTraits) and name in cls.__class_traits__


class TraitDocumenter(ClassLevelDocumenter):
    """ Specialized Documenter subclass for trait attributes.

    The class defines a new documenter that recovers the trait definition
    signature of module level and class level traits.

    To use the documenter, append the module path in the extension
    attribute of the `conf.py`.

    .. warning::

        Using the TraitDocumenter in conjunction with TraitsDoc is not
        advised.

    """

    objtype = 'traitattribute'
    directivetype = 'attribute'
    member_order = 60

    # must be higher than other attribute documenters
    priority = 12

    @classmethod
    def can_document_member(cls, member, membername, isattr, parent):
        """ Check that the documented member is a trait instance.
        """
        return (
            isattr and
            issubclass(type(member), TraitType) or
            is_class_trait(membername, parent.object))

    def document_members(self, all_members=False):
        # Trait attributes have no members """
        pass

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

    def import_object(self):
        """ Get the Trait object.

        Notes
        -----
        Code adapted from autodoc.Documenter.import_object.

        """
        try:
            __import__(self.modname)
            current = self.module = sys.modules[self.modname]
            for part in self.objpath[:-1]:
                current = self.get_attr(current, part)
            name = self.objpath[-1]
            self.object_name = name
            self.object = None
            self.parent = current
            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 as err:
            if self.env.app and not self.env.app.quiet:
                self.env.app.info(traceback.format_exc().rstrip())
            msg = (
                'autodoc can\'t import/find {0} {r1}, it reported error: '
                '"{2}", please check your spelling and sys.path')
            self.directive.warn(msg.format(
                self.objtype, str(self.fullname), err))
            self.env.note_reread()
            return False

    def add_directive_header(self, sig):
        """ Add the sphinx directives.

        Add the 'attribute' directive with the annotation option
        set to the trait definition.

        """
        ClassLevelDocumenter.add_directive_header(self, sig)
        definition = self.get_trait_definition()
        self.add_line(
            '   :annotation: = {0}'.format(definition), '<autodoc>')

    def get_trait_definition(self):
        """ Retrieve the Trait attribute definition
        """
        # Get the class source and tokenize it.
        source = inspect.getsource(self.parent)

        nodes = ast.parse(source)
        for node in ast.iter_child_nodes(nodes):
            if isinstance(node, ClassDef):
                parent_node = node
                break
        else:
            return ''

        for node in ast.iter_child_nodes(parent_node):
            if isinstance(node, Assign):
                name = node.targets[0]
                if name.id == self.object_name:
                    break
        else:
            return ''

        endlineno = name.lineno
        for item in ast.walk(node):
            if hasattr(item, 'lineno'):
                endlineno = max(endlineno, item.lineno)

        definition_lines = [
            line.strip()
            for line in source.splitlines()[name.lineno-1:endlineno]]
        definition = ''.join(definition_lines)
        equal = definition.index('=')
        return definition[equal + 1:].lstrip()
#---------------------------------------------------------------------------- # #  Copyright (c) 2014, Enthought, Inc. #  All rights reserved. # #  This software is provided without warranty under the terms of the BSD #  license included in /LICENSE.txt and may be redistributed only #  under the conditions described in the aforementioned license.  The license #  is also available online at http://www.enthought.com/licenses/BSD.txt # #  Thanks for using Enthought open source! # #---------------------------------------------------------------------------- Check if the name is in the list of class defined traits of ``cls``. Specialized Documenter subclass for trait attributes.      The class defines a new documenter that recovers the trait definition     signature of module level and class level traits.      To use the documenter, append the module path in the extension     attribute of the `conf.py`.      .. warning::          Using the TraitDocumenter in conjunction with TraitsDoc is not         advised. # must be higher than other attribute documenters Check that the documented member is a trait instance. # Trait attributes have no members """ # Never try to get a docstring from the trait object. Get the Trait object.          Notes         -----         Code adapted from autodoc.Documenter.import_object. # this used to only catch SyntaxError, ImportError and # AttributeError, but importing modules with side effects can raise # all kinds of errors. Add the sphinx directives.          Add the 'attribute' directive with the annotation option         set to the trait definition. Retrieve the Trait attribute definition # Get the class source and tokenize it.