source: rtems-central/rtemsqual/content.py @ 28b8f50

Last change on this file since 28b8f50 was 28b8f50, checked in by Sebastian Huber <sebastian.huber@…>, on Mar 17, 2020 at 2:45:40 PM

content: Add helper to accept a string or a list

  • Property mode set to 100644
File size: 8.2 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
127def _make_list(value):
128    if not isinstance(value, list):
129        return [value]
130    return value
131
132
133class SphinxContent(object):
134    """ This class builds Sphinx content. """
135    def __init__(self):
136        self._content = ""
137        self._license = "CC-BY-SA-4.0"
138        self._copyrights = Copyrights()
139
140    @property
141    def content(self):
142        """ Returns the content. """
143        return self._content
144
145    def add_label(self, label):
146        """ Adds a label to the content. """
147        self._content += ".. _" + label.strip() + ":\n"
148
149    def add_header(self, name, level="="):
150        """ Adds a header to the content. """
151        name = name.strip()
152        self._content += name + "\n" + level * len(name) + "\n"
153
154    def add_blank_line(self):
155        """ Adds a blank line to the content. """
156        self._content += "\n"
157
158    def add_line(self, line, indent=0):
159        """ Adds a line to the content. """
160        if line:
161            self._content += indent * "    " + line + "\n"
162        else:
163            self._content += "\n"
164
165    def add_lines(self, lines, indent=0):
166        """ Adds a lines to the content. """
167        for line in _make_lines(lines):
168            self.add_line(line, indent)
169
170    def add_index_entries(self, entries):
171        """ Adds a list of index entries the content. """
172        first = True
173        for entry in _make_list(entries):
174            if first:
175                first = False
176                self.add_blank_line()
177            self._content += ".. index:: " + entry + "\n"
178
179    def add_definition_item(self, name, lines, indent=0):
180        """ Adds a definition item the content. """
181        first = True
182        for line in lines:
183            if first:
184                first = False
185                self.add_blank_line()
186                self.add_line(name, indent)
187            self.add_line(line, indent=indent + 1)
188
189    def register_license(self, the_license):
190        """ Registers a licence for the content. """
191        licenses = re.split(r"\s+OR\s+", the_license)
192        if self._license not in licenses:
193            raise ValueError(the_license)
194
195    def register_copyright(self, statement):
196        """ Registers a copyright statement for the content. """
197        self._copyrights.register(statement)
198
199    def add_licence_and_copyrights(self):
200        """
201        Adds a licence and copyright block to the content according to the
202        registered licenses and copyrights.
203        """
204        spdx = f".. SPDX-License-Identifier: {self._license}\n"
205        statements = "\n.. ".join(self._copyrights.get_statements())
206        if statements:
207            self._content = f"{spdx}\n.. {statements}\n\n{self._content}"
208        else:
209            self._content = f"{spdx}\n{self._content}"
210
211    def write(self, path):
212        """ Writes the content to the file specified by the path. """
213        os.makedirs(os.path.dirname(path), exist_ok=True)
214        with open(path, "w+") as out:
215            out.write(self._content)
216
217
218class MacroToSphinx(object):
219    """ This class expands specification item macros to Sphinx markup. """
220    def __init__(self):
221        self._terms = {}
222
223    def set_terms(self, terms):
224        """ Sets the glossary of terms used for macro expansion. """
225        self._terms = terms
226
227    def substitute(self, text):
228        """
229        Substitutes all specification item macros contained in the text.
230        """
231        return re.sub(r"@@|@([a-z]+){([^}]+)}", self, text)
232
233    def __call__(self, match):
234        name = match.group(1)
235        if name:
236            roles = {
237                "term":
238                lambda x: ":term:`" + self._terms[x]["glossary-term"] + "`"
239            }
240            return roles[name](match.group(2))
241        assert match.group(0) == "@@"
242        return "@"
Note: See TracBrowser for help on using the repository browser.