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

Last change on this file since c0ac12a was c0ac12a, checked in by Sebastian Huber <sebastian.huber@…>, on Feb 25, 2020 at 12:54:17 PM

Initial import

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