Completed
Pull Request — master (#2423)
by
unknown
01:59
created

DocstyleDefinition   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 188
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 188
rs 10
wmc 19

7 Methods

Rating   Name   Duplication   Size   Complexity  
C __init__() 0 34 7
A docstyle() 0 9 1
A get_from_docstyle_settings() 0 11 3
A metadata() 0 11 1
B markers() 0 38 1
A language() 0 9 1
C load() 0 72 8
1
from collections import Iterable, namedtuple
2
import os.path
3
4
from coala_decorators.decorators import (
5
    enforce_signature, generate_eq, generate_repr)
6
from coalib.parsing.ConfParser import ConfParser
7
8
9
@generate_repr()
10
@generate_eq("language", "docstyle", "markers")
11
class DocstyleDefinition:
12
    """
13
    The DocstyleDefinition class holds values that identify a certain type of
14
    documentation comment (for which language, documentation style/tool used
15
    etc.).
16
    """
17
    Metadata = namedtuple("Metadata", "param_start, param_end, return_sep")
18
19
    @enforce_signature
20
    def __init__(self, language: str, docstyle: str, markers: (Iterable, str),
21
                 metadata: Metadata):
22
        """
23
        Instantiates a new DocstyleDefinition.
24
25
        :param language: The case insensitive programming language of the
26
                         documentation comment, e.g. ``"CPP"`` for C++ or
27
                         ``"PYTHON3"``.
28
        :param docstyle: The case insensitive documentation style/tool used
29
                         to document code, e.g. ``"default"`` or ``"doxygen"``.
30
        :param markers:  An iterable of marker/delimiter string iterables
31
                         or a single marker/delimiter string iterable that
32
                         identify a documentation comment. See ``markers``
33
                         property for more details on markers.
34
        """
35
        self._language = language.lower()
36
        self._docstyle = docstyle.lower()
37
38
        # Check and modify tuple if only one marker_set exists.
39
        markers = tuple(markers)
40
        if len(markers) == 3 and all(isinstance(x, str) for x in markers):
41
            markers = (markers,)
42
43
        self._markers = tuple(tuple(marker_set) for marker_set in markers)
44
45
        # Check marker set dimensions.
46
        for marker_set in self._markers:
47
            length = len(marker_set)
48
            if length != 3:
49
                raise ValueError("Length of a given marker set was not 3 (was "
50
                                 "actually {}).".format(length))
51
52
        self._metadata = metadata
53
54
    @property
55
    def language(self):
56
        """
57
        The programming language.
58
59
        :return: A lower-case string defining the programming language (i.e.
60
                 "cpp" or "python").
61
        """
62
        return self._language
63
64
    @property
65
    def docstyle(self):
66
        """
67
        The documentation style/tool used to document code.
68
69
        :return: A lower-case string defining the docstyle (i.e. "default" or
70
                 "doxygen").
71
        """
72
        return self._docstyle
73
74
    @property
75
    def markers(self):
76
        """
77
        A tuple of marker sets that identify a documentation comment.
78
79
        Marker sets consist of 3 entries where the first is the start-marker,
80
        the second one the each-line marker and the last one the end-marker.
81
        For example a marker tuple with a single marker set
82
        ``(("/**", "*", "*/"),)`` would match following documentation comment:
83
84
        ::
85
86
            /**
87
             * This is documentation.
88
             */
89
90
        It's also possible to supply an empty each-line marker
91
        (``("/**", "", "*/")``):
92
93
        ::
94
95
            /**
96
             This is more documentation.
97
             */
98
99
        Markers are matched "greedy", that means it will match as many
100
        each-line markers as possible. I.e. for ``("///", "///", "///")``):
101
102
        ::
103
104
            /// Brief documentation.
105
            ///
106
            /// Detailed documentation.
107
108
        :return: A tuple of marker/delimiter string tuples that identify a
109
                 documentation comment.
110
        """
111
        return self._markers
112
113
    @property
114
    def metadata(self):
115
        """
116
        A namedtuple of documentation symbols.
117
118
        These documentation symbols are used to define certain symbols that
119
        define a documentation comment.
120
121
        Currently consists of ``param_start``, ``param_end`` and ``return_sep``.
122
        """
123
        return self._metadata
124
125
    @classmethod
126
    @enforce_signature
127
    def load(cls, language: str, docstyle: str, coalang_dir=None):
128
        """
129
        Loads a ``DocstyleDefinition`` from the coala docstyle definition files.
130
131
        This function considers all settings inside the according coalang-files
132
        as markers.
133
134
        :param language:           The case insensitive programming language of
135
                                   the documentation comment as a string.
136
        :param docstyle:           The case insensitive documentation
137
                                   style/tool used to document code, e.g.
138
                                   ``"default"`` or ``"doxygen"``.
139
        :param coalang_dir:        Path to directory with coalang docstyle
140
                                   definition files. This replaces the default
141
                                   path if given.
142
        :raises FileNotFoundError: Raised when the given docstyle was not
143
                                   found.
144
        :raises KeyError:          Raised when the given language is not
145
                                   defined for given docstyle.
146
        :return:                   The ``DocstyleDefinition`` for given language
147
                                   and docstyle.
148
        """
149
150
        docstyle = docstyle.lower()
151
152
        language_config_parser = ConfParser(remove_empty_iter_elements=False)
153
154
        coalang_file = os.path.join(
155
            coalang_dir or os.path.dirname(__file__), docstyle + ".coalang")
156
157
        try:
158
            docstyle_settings = language_config_parser.parse(coalang_file)
159
        except FileNotFoundError:
160
            raise FileNotFoundError("Docstyle definition " + repr(docstyle) +
161
                                    " not found.")
162
163
        language = language.lower()
164
165
        try:
166
            docstyle_settings = docstyle_settings[language]
167
        except KeyError:
168
            raise KeyError("Language {!r} is not defined for docstyle {!r}."
169
                           .format(language, docstyle))
170
171
        def get_from_docstyle_settings(*args):
172
            values = list()
173
            for req_setting in args:
174
                setting = dict(
175
                    docstyle_settings.contents.items()).get(req_setting)
176
                try:
177
                    setting = setting.value
178
                except AttributeError:
179
                    setting = ""
180
                values.append(setting)
181
            return values
182
183
        values = get_from_docstyle_settings('param_start', 'param_end',
184
                                            'return_sep')
185
186
        metadata = cls.Metadata._make(values)
187
188
        ignored_settings = ("comment", "param_start", "param_end", "return")
189
190
        marker_sets = (tuple(value)
191
                       for key, value in
192
                       filter(lambda kv: not kv[0].startswith(
193
                           ignored_settings),
194
                       docstyle_settings.contents.items()))
195
196
        return cls(language, docstyle, marker_sets, metadata)
197