source: rtems-central/rtemsqual/content.py @ a400958

Last change on this file since a400958 was a400958, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 17, 2020 at 1:29:26 PM

content: Add method to add lines

  • Property mode set to 100644
File size: 8.1 KB
Line 
1# SPDX-License-Identifier: BSD-2-Clause
2""" This module provides classes for content generation. """
3
4# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26
27# pylint: disable=useless-object-inheritance
28
29import os
30import re
31
32
33class Copyright(object):
34    """
35    This class represents a copyright holder with its years of substantial
36    contributions.
37    """
38    def __init__(self, holder):
39        self._holder = holder
40        self._years = set()
41
42    def add_year(self, year: str):
43        """
44        Adds a year to the set of substantial contributions of this copyright
45        holder.
46        """
47        self._years.add(year)
48
49    def get_statement(self) -> str:
50        """ Returns a copyright statement. """
51        line = "Copyright (C)"
52        years = sorted(self._years)
53        year_count = len(years)
54        if year_count == 1:
55            line += " " + years[0]
56        elif year_count > 1:
57            line += " " + years[0] + ", " + years[-1]
58        line += " " + self._holder
59        return line
60
61    def __lt__(self, other: "Copyright") -> bool:
62        # pylint: disable=protected-access
63        if self._years and other._years:
64            self_first_year = sorted(self._years)[0]
65            other_first_year = sorted(other._years)[0]
66            if self_first_year == other_first_year:
67                return self._holder > other._holder
68            return self_first_year > other_first_year
69        if self._years or other._years:
70            return True
71        return self._holder > other._holder
72
73
74class Copyrights(object):
75    """ This class represents a set of copyright holders. """
76    def __init__(self):
77        self.copyrights = {}
78
79    def register(self, statement):
80        """ Registers a copyright statement. """
81        match = re.search(
82            r"^\s*Copyright\s+\(C\)\s+([0-9]+),\s*([0-9]+)\s+(.+)\s*$",
83            statement,
84            flags=re.I,
85        )
86        if match:
87            holder = match.group(3)
88            the_copyright = self.copyrights.setdefault(holder,
89                                                       Copyright(holder))
90            the_copyright.add_year(match.group(1))
91            the_copyright.add_year(match.group(2))
92            return
93        match = re.search(
94            r"^\s*Copyright\s+\(C\)\s+([0-9]+)\s+(.+)\s*$",
95            statement,
96            flags=re.I,
97        )
98        if match:
99            holder = match.group(2)
100            the_copyright = self.copyrights.setdefault(holder,
101                                                       Copyright(holder))
102            the_copyright.add_year(match.group(1))
103            return
104        match = re.search(r"^\s*Copyright\s+\(C\)\s+(.+)\s*$",
105                          statement,
106                          flags=re.I)
107        if match:
108            holder = match.group(1)
109            self.copyrights.setdefault(holder, Copyright(holder))
110            return
111        raise ValueError(statement)
112
113    def get_statements(self):
114        """ Returns all registered copyright statements as a sorted list. """
115        statements = []
116        for the_copyright in sorted(self.copyrights.values()):
117            statements.append(the_copyright.get_statement())
118        return statements
119
120
121def _make_lines(lines):
122    if not isinstance(lines, list):
123        return lines.strip("\n").split("\n")
124    return lines
125
126
127class SphinxContent(object):
128    """ This class builds Sphinx content. """
129    def __init__(self):
130        self._content = ""
131        self._license = "CC-BY-SA-4.0"
132        self._copyrights = Copyrights()
133
134    @property
135    def content(self):
136        """ Returns the content. """
137        return self._content
138
139    def add_label(self, label):
140        """ Adds a label to the content. """
141        self._content += ".. _" + label.strip() + ":\n"
142
143    def add_header(self, name, level="="):
144        """ Adds a header to the content. """
145        name = name.strip()
146        self._content += name + "\n" + level * len(name) + "\n"
147
148    def add_blank_line(self):
149        """ Adds a blank line to the content. """
150        self._content += "\n"
151
152    def add_line(self, line, indent=0):
153        """ Adds a line to the content. """
154        if line:
155            self._content += indent * "    " + line + "\n"
156        else:
157            self._content += "\n"
158
159    def add_lines(self, lines, indent=0):
160        """ Adds a lines to the content. """
161        for line in _make_lines(lines):
162            self.add_line(line, indent)
163
164    def add_index_entries(self, entries):
165        """ Adds a list of index entries the content. """
166        first = True
167        for entry in entries:
168            if first:
169                first = False
170                self.add_blank_line()
171            self._content += ".. index:: " + entry + "\n"
172
173    def add_definition_item(self, name, lines, indent=0):
174        """ Adds a definition item the content. """
175        first = True
176        for line in lines:
177            if first:
178                first = False
179                self.add_blank_line()
180                self.add_line(name, indent)
181            self.add_line(line, indent=indent + 1)
182
183    def register_license(self, the_license):
184        """ Registers a licence for the content. """
185        licenses = re.split(r"\s+OR\s+", the_license)
186        if self._license not in licenses:
187            raise ValueError(the_license)
188
189    def register_copyright(self, statement):
190        """ Registers a copyright statement for the content. """
191        self._copyrights.register(statement)
192
193    def add_licence_and_copyrights(self):
194        """
195        Adds a licence and copyright block to the content according to the
196        registered licenses and copyrights.
197        """
198        spdx = f".. SPDX-License-Identifier: {self._license}\n"
199        statements = "\n.. ".join(self._copyrights.get_statements())
200        if statements:
201            self._content = f"{spdx}\n.. {statements}\n\n{self._content}"
202        else:
203            self._content = f"{spdx}\n{self._content}"
204
205    def write(self, path):
206        """ Writes the content to the file specified by the path. """
207        os.makedirs(os.path.dirname(path), exist_ok=True)
208        with open(path, "w+") as out:
209            out.write(self._content)
210
211
212class MacroToSphinx(object):
213    """ This class expands specification item macros to Sphinx markup. """
214    def __init__(self):
215        self._terms = {}
216
217    def set_terms(self, terms):
218        """ Sets the glossary of terms used for macro expansion. """
219        self._terms = terms
220
221    def substitute(self, text):
222        """
223        Substitutes all specification item macros contained in the text.
224        """
225        return re.sub(r"@@|@([a-z]+){([^}]+)}", self, text)
226
227    def __call__(self, match):
228        name = match.group(1)
229        if name:
230            roles = {
231                "term":
232                lambda x: ":term:`" + self._terms[x]["glossary-term"] + "`"
233            }
234            return roles[name](match.group(2))
235        assert match.group(0) == "@@"
236        return "@"
Note: See TracBrowser for help on using the repository browser.