-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
DocstyleDefinition.py
210 lines (166 loc) · 7.76 KB
/
DocstyleDefinition.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
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()