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

DocstyleDefinition.get_from_docstyle_settings()   A

Complexity

Conditions 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
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",
18
                                       "return_sep"))
19
20
    @enforce_signature
21
    def __init__(self, language: str, docstyle: str, markers: (Iterable, str),
22
                 metadata: Metadata):
23
        """
24
        Instantiates a new DocstyleDefinition.
25
26
        :param language: The case insensitive programming language of the
27
                         documentation comment, e.g. ``"CPP"`` for C++ or
28
                         ``"PYTHON3"``.
29
        :param docstyle: The case insensitive documentation style/tool used
30
                         to document code, e.g. ``"default"`` or ``"doxygen"``.
31
        :param markers:  An iterable of marker/delimiter string iterables
32
                         or a single marker/delimiter string iterable that
33
                         identify a documentation comment. See ``markers``
34
                         property for more details on markers.
35
        :param metadata: A namedtuple consisting of certain attributes that
36
                         are present in the documentation comment.
37
        """
38
        self._language = language.lower()
39
        self._docstyle = docstyle.lower()
40
41
        # Check and modify tuple if only one marker_set exists.
42
        markers = tuple(markers)
43
        if len(markers) == 3 and all(isinstance(x, str) for x in markers):
44
            markers = (markers,)
45
46
        self._markers = tuple(tuple(marker_set) for marker_set in markers)
47
48
        # Check marker set dimensions.
49
        for marker_set in self._markers:
50
            length = len(marker_set)
51
            if length != 3:
52
                raise ValueError("Length of a given marker set was not 3 (was "
53
                                 "actually {}).".format(length))
54
55
        self._metadata = metadata
56
57
    @property
58
    def language(self):
59
        """
60
        The programming language.
61
62
        :return: A lower-case string defining the programming language (i.e.
63
                 "cpp" or "python").
64
        """
65
        return self._language
66
67
    @property
68
    def docstyle(self):
69
        """
70
        The documentation style/tool used to document code.
71
72
        :return: A lower-case string defining the docstyle (i.e. "default" or
73
                 "doxygen").
74
        """
75
        return self._docstyle
76
77
    @property
78
    def markers(self):
79
        """
80
        A tuple of marker sets that identify a documentation comment.
81
82
        Marker sets consist of 3 entries where the first is the start-marker,
83
        the second one the each-line marker and the last one the end-marker.
84
        For example a marker tuple with a single marker set
85
        ``(("/**", "*", "*/"),)`` would match following documentation comment:
86
87
        ::
88
89
            /**
90
             * This is documentation.
91
             */
92
93
        It's also possible to supply an empty each-line marker
94
        (``("/**", "", "*/")``):
95
96
        ::
97
98
            /**
99
             This is more documentation.
100
             */
101
102
        Markers are matched "greedy", that means it will match as many
103
        each-line markers as possible. I.e. for ``("///", "///", "///")``):
104
105
        ::
106
107
            /// Brief documentation.
108
            ///
109
            /// Detailed documentation.
110
111
        :return: A tuple of marker/delimiter string tuples that identify a
112
                 documentation comment.
113
        """
114
        return self._markers
115
116
    @property
117
    def metadata(self):
118
        """
119
        A namedtuple of certain attributes present in the documentation.
120
121
        These attributes are used to define parts of the documentation.
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
        metadata_settings = ("param_start", "param_end", "return")
184
185
        values = get_from_docstyle_settings('param_start', 'param_end',
186
                                            'return_sep')
187
188
        metadata = cls.Metadata(*values)
189
190
        ignored_settings = ("comment",) + metadata_settings
191
192
        marker_sets = (tuple(value)
193
                       for key, value in
194
                       docstyle_settings.contents.items()
195
                       if not key.startswith(ignored_settings))
196
197
        return cls(language, docstyle, marker_sets, metadata)
198