coalib.bearlib.languages.documentation.DocstyleDefinition — coala 0.11.0.rc1 documentation

# Source code for coalib.bearlib.languages.documentation.DocstyleDefinition

    from collections import Iterable, namedtuple
    from glob import iglob
    import os.path

    from coala_utils.decorators import (
        enforce_signature, generate_eq, generate_repr)
    from coalib.parsing.ConfParser import ConfParser

    @generate_repr()
    @generate_eq('language', 'docstyle', 'markers')
    class DocstyleDefinition:
        """
        The DocstyleDefinition class holds values that identify a certain type of
        documentation comment (for which language, documentation style/tool used
        etc.).
        """
        Metadata = namedtuple('Metadata', ('param_start', 'param_end',
                                           'return_sep'))

        @enforce_signature
        def __init__(self, language: str, docstyle: str, markers: (Iterable, str),
                     metadata: Metadata):
            """
            Instantiates a new DocstyleDefinition.

            :param language: The case insensitive programming language of the
                             documentation comment, e.g. ``"CPP"`` for C++ or
                             ``"PYTHON3"``.
            :param docstyle: The case insensitive documentation style/tool used
                             to document code, e.g. ``"default"`` or ``"doxygen"``.
            :param markers:  An iterable of marker/delimiter string iterables
                             or a single marker/delimiter string iterable that
                             identify a documentation comment. See ``markers``
                             property for more details on markers.
            :param metadata: A namedtuple consisting of certain attributes that
                             form the layout of the certain documentation comment
                             e.g. ``param_start`` defining the start symbol of
                             the parameter fields and ``param_end`` defining the
                             end.
            """
            self._language = language.lower()
            self._docstyle = docstyle.lower()

            # Check and modify tuple if only one marker_set exists.
            markers = tuple(markers)
            if len(markers) == 3 and all(isinstance(x, str) for x in markers):
                markers = (markers,)

            self._markers = tuple(tuple(marker_set) for marker_set in markers)

            # Check marker set dimensions.
            for marker_set in self._markers:
                length = len(marker_set)
                if length != 3:
                    raise ValueError('Length of a given marker set was not 3 (was '
                                     'actually {}).'.format(length))

            self._metadata = metadata

        @property
        def language(self):
            """
            The programming language.

            :return: A lower-case string defining the programming language (i.e.
                     "cpp" or "python").
            """
            return self._language

        @property
        def docstyle(self):
            """
            The documentation style/tool used to document code.

            :return: A lower-case string defining the docstyle (i.e. "default" or
                     "doxygen").
            """
            return self._docstyle

        @property
        def markers(self):
            """
            A tuple of marker sets that identify a documentation comment.

            Marker sets consist of 3 entries where the first is the start-marker,
            the second one the each-line marker and the last one the end-marker.
            For example a marker tuple with a single marker set
            ``(("/**", "*", "*/"),)`` would match following documentation comment:

            ::

                /**
                 * This is documentation.
                 */

            It's also possible to supply an empty each-line marker
            (``("/**", "", "*/")``):

            ::

                /**
                 This is more documentation.
                 */

            Markers are matched "greedy", that means it will match as many
            each-line markers as possible. I.e. for ``("///", "///", "///")``):

            ::

                /// Brief documentation.
                ///
                /// Detailed documentation.

            :return: A tuple of marker/delimiter string tuples that identify a
                     documentation comment.
            """
            return self._markers

        @property
        def metadata(self):
            """
            A namedtuple of certain attributes present in the documentation.

            These attributes are used to define parts of the documentation.
            """
            return self._metadata

        @classmethod
        @enforce_signature
        def load(cls, language: str, docstyle: str, coalang_dir=None):
            """
            Loads a ``DocstyleDefinition`` from the coala docstyle definition files.

            This function considers all settings inside the according coalang-files
            as markers, except ``param_start``, ``param_end`` and ``return_sep``
            which are considered as special metadata markers.

            .. note::

                When placing new coala docstyle definition files, these must
                consist of only lowercase letters and end with ``.coalang``!

            :param language:           The case insensitive programming language of
                                       the documentation comment as a string.
            :param docstyle:           The case insensitive documentation
                                       style/tool used to document code, e.g.
                                       ``"default"`` or ``"doxygen"``.
            :param coalang_dir:        Path to directory with coalang docstyle
                                       definition files. This replaces the default
                                       path if given.
            :raises FileNotFoundError: Raised when the given docstyle was not
                                       found.
            :raises KeyError:          Raised when the given language is not
                                       defined for given docstyle.
            :return:                   The ``DocstyleDefinition`` for given language
                                       and docstyle.
            """

            docstyle = docstyle.lower()

            language_config_parser = ConfParser(remove_empty_iter_elements=False)

            coalang_file = os.path.join(
                coalang_dir or os.path.dirname(__file__), docstyle + '.coalang')

            try:
                docstyle_settings = language_config_parser.parse(coalang_file)
            except FileNotFoundError:
                raise FileNotFoundError('Docstyle definition ' + repr(docstyle) +
                                        ' not found.')

            language = language.lower()

            try:
                docstyle_settings = docstyle_settings[language]
            except KeyError:
                raise KeyError('Language {!r} is not defined for docstyle {!r}.'
                               .format(language, docstyle))

            metadata_settings = ('param_start', 'param_end', 'return_sep')

            metadata = cls.Metadata(*(str(docstyle_settings.get(req_setting, ''))
                                      for req_setting in metadata_settings))

            marker_sets = (tuple(value)
                           for key, value in
                           docstyle_settings.contents.items()
                           if key not in metadata_settings and
                           not key.startswith('comment'))

            return cls(language, docstyle, marker_sets, metadata)

        @staticmethod
        def get_available_definitions():
            """
            Returns a sequence of pairs with ``(docstyle, language)`` which are
            available when using ``load()``.

            :return: A sequence of pairs with ``(docstyle, language)``.
            """
            language_config_parser = ConfParser(remove_empty_iter_elements=False)
            pattern = os.path.join(os.path.dirname(__file__), '*.coalang')

            for coalang_file in iglob(pattern):
                docstyle = os.path.splitext(os.path.basename(coalang_file))[0]
                # Ignore files that are not lowercase, as coalang files have to be.
                if docstyle.lower() == docstyle:
                    for language in language_config_parser.parse(coalang_file):
                        yield docstyle, language.lower()