diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2020-05-27 14:05:36 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2020-05-28 10:38:23 +0200 |
commit | c8e568a1caf99693515e4e0ca798d3e6c8b2747c (patch) | |
tree | 3bb3a7df6e55032de4ec42d696e86b815e37652f | |
parent | spec: Add interface domain items (diff) | |
download | rtems-central-c8e568a1caf99693515e4e0ca798d3e6c8b2747c.tar.bz2 |
sphinxcontent: Move Sphinx content to new module
-rw-r--r-- | rtemsqual/applconfig.py | 2 | ||||
-rw-r--r-- | rtemsqual/content.py | 181 | ||||
-rw-r--r-- | rtemsqual/glossary.py | 2 | ||||
-rw-r--r-- | rtemsqual/specdoc.py | 2 | ||||
-rw-r--r-- | rtemsqual/sphinxcontent.py | 205 | ||||
-rw-r--r-- | rtemsqual/tests/test_content_sphinx.py | 106 |
6 files changed, 267 insertions, 231 deletions
diff --git a/rtemsqual/applconfig.py b/rtemsqual/applconfig.py index 7b7b3e17..2a4074f2 100644 --- a/rtemsqual/applconfig.py +++ b/rtemsqual/applconfig.py @@ -26,7 +26,7 @@ from typing import Any, Dict, List, Optional -from rtemsqual.content import SphinxContent +from rtemsqual.sphinxcontent import SphinxContent from rtemsqual.items import Item, ItemCache ItemMap = Dict[str, Item] diff --git a/rtemsqual/content.py b/rtemsqual/content.py index 8aa0cb5b..bf07ebdd 100644 --- a/rtemsqual/content.py +++ b/rtemsqual/content.py @@ -32,7 +32,7 @@ import textwrap from typing import Any, Callable, ContextManager, Dict, Iterable, Iterator, \ List, NamedTuple, Optional, Set, Tuple, Union -from rtemsqual.items import Item, ItemMapper +from rtemsqual.items import Item AddContext = Callable[["Content"], ContextManager[None]] GenericContent = Union[str, List[str], "Content"] @@ -128,7 +128,8 @@ class Copyrights: return statements -def _make_lines(content: GenericContent) -> List[str]: +def make_lines(content: GenericContent) -> List[str]: + """ Makes a list of lines from a generic content. """ if isinstance(content, str): return content.strip("\n").split("\n") if isinstance(content, list): @@ -146,21 +147,11 @@ def _indent(lines: List[str], indent: str, return lines -def _to_camel_case(name: str) -> str: - return name[0].upper() + re.sub( - r"[^a-zA-Z0-9]", "X", - re.sub(r"[ \n\t]+([a-zA-Z0-9])", lambda match: match.group(1).upper(), - name[1:])) - - @contextmanager def _add_context(_content: "Content") -> Iterator[None]: yield -_HEADER_LEVELS = ["#", "*", "=", "-", "^", "\""] - - class Content: """ This class builds content. """ @@ -193,12 +184,12 @@ class Content: def append(self, content: GenericContent) -> None: """ Appends the content. """ self._lines.extend( - _indent(_make_lines(content), self._indent, + _indent(make_lines(content), self._indent, self._empty_line_indent)) def prepend(self, content: GenericContent) -> None: """ Prepends the content. """ - self._lines[0:0] = _indent(_make_lines(content), self._indent, + self._lines[0:0] = _indent(make_lines(content), self._indent, self._empty_line_indent) def add(self, @@ -209,7 +200,7 @@ class Content: """ if not content: return - lines = _make_lines(content) + lines = make_lines(content) index = 0 for line in lines: if line: @@ -405,166 +396,6 @@ class Content: out.write(str(self)) -class SphinxContent(Content): - """ This class builds Sphinx content. """ - def __init__(self, section_level: int = 2): - super().__init__("CC-BY-SA-4.0", True) - self._tab = " " - self._section_level = section_level - - def get_reference(self, label: str, name: Optional[str] = None) -> str: - """ Returns the reference to the specified label. """ - # pylint: disable=no-self-use - if name: - return f":ref:`{name} <{label}>`" - return f":ref:`{label}`" - - def add_label(self, label: str) -> None: - """ Adds a label. """ - self.add(".. _" + label.strip() + ":") - - def get_section_label(self, name: str, prefix: str = "Section") -> str: - """ Returns the section label for the specified section name. """ - # pylint: disable=no-self-use - return prefix + _to_camel_case(name.strip()) - - def add_header(self, name, level=2) -> None: - """ Adds a header. """ - name = name.strip() - self.add([name, _HEADER_LEVELS[level] * len(name)]) - - def add_header_with_label(self, - name: str, - level: int = 2, - label_prefix: str = "Section") -> str: - """ Adds a header with label. """ - label = self.get_section_label(name, label_prefix) - self.add_label(label) - self.add_header(name, level) - return label - - def add_index_entries(self, entries) -> None: - """ Adds a list of index entries the content. """ - self.add([".. index:: " + entry for entry in _make_lines(entries)]) - - def add_definition_item(self, name, lines) -> None: - """ Adds a definition item the content. """ - @contextmanager - def _definition_item_context(content: Content) -> Iterator[None]: - content.append(name) - content.push_indent() - yield - content.pop_indent() - - self.add(lines, _definition_item_context) - - def open_directive(self, - name: str, - value: Optional[str] = None, - options: Optional[List[str]] = None) -> None: - """ Opens a directive. """ - value = " " + value if value else "" - self.add(f".. {name.strip()}::{value}") - self.push_indent() - self.add(options) - self.gap = True - - def close_directive(self) -> None: - """ Closes a directive. """ - self.pop_indent() - - @contextmanager - def directive(self, - name: str, - value: Optional[str] = None, - options: Optional[List[str]] = None): - """ Opens a directive context. """ - self.open_directive(name, value, options) - yield - self.close_directive() - - def open_section(self, name: str, label_prefix: str = "Section") -> str: - """ Opens a section. """ - label = self.add_header_with_label(name, self._section_level, - label_prefix) - self._section_level += 1 - return label - - def close_section(self) -> None: - """ Closes a section. """ - self._section_level -= 1 - - @contextmanager - def section(self, - name: str, - label_prefix: str = "Section") -> Iterator[str]: - """ Opens a section context. """ - yield self.open_section(name, label_prefix) - self.close_section() - - def add_list_item(self, content: GenericContent) -> None: - """ Adds a list item. """ - self.wrap(content, initial_indent="* ", subsequent_indent=" ") - - def add_list(self, - items: GenericContentIterable, - prologue: Optional[GenericContent] = None, - epilogue: Optional[GenericContent] = None, - add_blank_line: bool = False) -> None: - """ Adds a list with introduction. """ - if items: - self.wrap(prologue) - for item in items: - self.add_list_item(item) - if add_blank_line: - self.add_blank_line() - self.wrap(epilogue) - - def open_list_item(self, content: GenericContent) -> None: - """ Opens a list item. """ - self.add(["* "]) - self.push_indent(" ") - self.gap = True - self.paste(content) - - def close_list_item(self) -> None: - """ Closes a list item. """ - self.pop_indent() - self.gap = True - - @contextmanager - def list_item(self, content: GenericContent) -> Iterator[None]: - """ Opens a list item context. """ - self.open_list_item(content) - yield - self.close_list_item() - - def add_licence_and_copyrights(self) -> None: - """ - Adds a licence and copyright block according to the registered licenses - and copyrights. - """ - statements = self._copyrights.get_statements() - if statements: - self.prepend("") - self.prepend([f".. {stm}" for stm in statements]) - self.prepend([f".. SPDX-License-Identifier: {self._license}", ""]) - - -class SphinxMapper(ItemMapper): - """ Sphinx mapper. """ - def __init__(self, item: Item): - super().__init__(item) - - def get_value(self, _item: Item, _path: str, value: Any, key: str, - _index: Optional[int]) -> Any: - """ Gets a value by key and optional index. """ - # pylint: disable=no-self-use - if key == "term": - return f":term:`{value[key]}`" - raise KeyError - - _BSD_2_CLAUSE_LICENSE = """Redistribution and use in source and binary \ forms, with or without modification, are permitted provided that the following conditions diff --git a/rtemsqual/glossary.py b/rtemsqual/glossary.py index 14448e0b..f05e3aee 100644 --- a/rtemsqual/glossary.py +++ b/rtemsqual/glossary.py @@ -28,7 +28,7 @@ import glob import re from typing import Any, Dict, Optional -from rtemsqual.content import SphinxContent, SphinxMapper +from rtemsqual.sphinxcontent import SphinxContent, SphinxMapper from rtemsqual.items import Item, ItemCache, ItemMapper ItemMap = Dict[str, Item] diff --git a/rtemsqual/specdoc.py b/rtemsqual/specdoc.py index 7a99e788..8dde1d41 100644 --- a/rtemsqual/specdoc.py +++ b/rtemsqual/specdoc.py @@ -26,7 +26,7 @@ from typing import Any, Dict, Iterator, Optional, Set, Tuple -from rtemsqual.content import SphinxContent +from rtemsqual.sphinxcontent import SphinxContent from rtemsqual.items import Item, ItemCache from rtemsqual.specverify import NAME diff --git a/rtemsqual/sphinxcontent.py b/rtemsqual/sphinxcontent.py new file mode 100644 index 00000000..fbcbd22f --- /dev/null +++ b/rtemsqual/sphinxcontent.py @@ -0,0 +1,205 @@ +# SPDX-License-Identifier: BSD-2-Clause +""" This module provides classes for Sphinx content generation. """ + +# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de) +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +from contextlib import contextmanager +import re +from typing import Any, Iterable, Iterator, List, Optional, Union + +from rtemsqual.content import Content, make_lines +from rtemsqual.items import Item, ItemMapper + +GenericContent = Union[str, List[str], "Content"] +GenericContentIterable = Union[Iterable[str], Iterable[List[str]], + Iterable[GenericContent]] + +_HEADER_LEVELS = ["#", "*", "=", "-", "^", "\""] + + +def _to_camel_case(name: str) -> str: + return name[0].upper() + re.sub( + r"[^a-zA-Z0-9]", "X", + re.sub(r"[ \n\t]+([a-zA-Z0-9])", lambda match: match.group(1).upper(), + name[1:])) + + +class SphinxContent(Content): + """ This class builds Sphinx content. """ + def __init__(self, section_level: int = 2): + super().__init__("CC-BY-SA-4.0", True) + self._tab = " " + self._section_level = section_level + + def get_reference(self, label: str, name: Optional[str] = None) -> str: + """ Returns the reference to the specified label. """ + # pylint: disable=no-self-use + if name: + return f":ref:`{name} <{label}>`" + return f":ref:`{label}`" + + def add_label(self, label: str) -> None: + """ Adds a label. """ + self.add(".. _" + label.strip() + ":") + + def get_section_label(self, name: str, prefix: str = "Section") -> str: + """ Returns the section label for the specified section name. """ + # pylint: disable=no-self-use + return prefix + _to_camel_case(name.strip()) + + def add_header(self, name, level=2) -> None: + """ Adds a header. """ + name = name.strip() + self.add([name, _HEADER_LEVELS[level] * len(name)]) + + def add_header_with_label(self, + name: str, + level: int = 2, + label_prefix: str = "Section") -> str: + """ Adds a header with label. """ + label = self.get_section_label(name, label_prefix) + self.add_label(label) + self.add_header(name, level) + return label + + def add_index_entries(self, entries) -> None: + """ Adds a list of index entries the content. """ + self.add([".. index:: " + entry for entry in make_lines(entries)]) + + def add_definition_item(self, name, lines) -> None: + """ Adds a definition item the content. """ + @contextmanager + def _definition_item_context(content: Content) -> Iterator[None]: + content.append(name) + content.push_indent() + yield + content.pop_indent() + + self.add(lines, _definition_item_context) + + def open_directive(self, + name: str, + value: Optional[str] = None, + options: Optional[List[str]] = None) -> None: + """ Opens a directive. """ + value = " " + value if value else "" + self.add(f".. {name.strip()}::{value}") + self.push_indent() + self.add(options) + self.gap = True + + def close_directive(self) -> None: + """ Closes a directive. """ + self.pop_indent() + + @contextmanager + def directive(self, + name: str, + value: Optional[str] = None, + options: Optional[List[str]] = None): + """ Opens a directive context. """ + self.open_directive(name, value, options) + yield + self.close_directive() + + def open_section(self, name: str, label_prefix: str = "Section") -> str: + """ Opens a section. """ + label = self.add_header_with_label(name, self._section_level, + label_prefix) + self._section_level += 1 + return label + + def close_section(self) -> None: + """ Closes a section. """ + self._section_level -= 1 + + @contextmanager + def section(self, + name: str, + label_prefix: str = "Section") -> Iterator[str]: + """ Opens a section context. """ + yield self.open_section(name, label_prefix) + self.close_section() + + def add_list_item(self, content: GenericContent) -> None: + """ Adds a list item. """ + self.wrap(content, initial_indent="* ", subsequent_indent=" ") + + def add_list(self, + items: GenericContentIterable, + prologue: Optional[GenericContent] = None, + epilogue: Optional[GenericContent] = None, + add_blank_line: bool = False) -> None: + """ Adds a list with introduction. """ + if items: + self.wrap(prologue) + for item in items: + self.add_list_item(item) + if add_blank_line: + self.add_blank_line() + self.wrap(epilogue) + + def open_list_item(self, content: GenericContent) -> None: + """ Opens a list item. """ + self.add(["* "]) + self.push_indent(" ") + self.gap = True + self.paste(content) + + def close_list_item(self) -> None: + """ Closes a list item. """ + self.pop_indent() + self.gap = True + + @contextmanager + def list_item(self, content: GenericContent) -> Iterator[None]: + """ Opens a list item context. """ + self.open_list_item(content) + yield + self.close_list_item() + + def add_licence_and_copyrights(self) -> None: + """ + Adds a licence and copyright block according to the registered licenses + and copyrights. + """ + statements = self._copyrights.get_statements() + if statements: + self.prepend("") + self.prepend([f".. {stm}" for stm in statements]) + self.prepend([f".. SPDX-License-Identifier: {self._license}", ""]) + + +class SphinxMapper(ItemMapper): + """ Sphinx mapper. """ + def __init__(self, item: Item): + super().__init__(item) + + def get_value(self, _item: Item, _path: str, value: Any, key: str, + _index: Optional[int]) -> Any: + """ Gets a value by key and optional index. """ + # pylint: disable=no-self-use + if key == "term": + return f":term:`{value[key]}`" + raise KeyError diff --git a/rtemsqual/tests/test_content_sphinx.py b/rtemsqual/tests/test_content_sphinx.py index 4f033077..43272c38 100644 --- a/rtemsqual/tests/test_content_sphinx.py +++ b/rtemsqual/tests/test_content_sphinx.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: BSD-2-Clause -""" Unit tests for the rtemsqual.content module. """ +""" Unit tests for the rtemsqual.sphinxcontent module. """ # Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) # @@ -26,29 +26,29 @@ import pytest -from rtemsqual.content import SphinxContent, SphinxMapper +from rtemsqual.sphinxcontent import SphinxContent, SphinxMapper from rtemsqual.items import Item, ItemCache, ItemMapper from rtemsqual.tests.util import create_item_cache_config_and_copy_spec def test_add_label(): - sc = SphinxContent() - sc.add_label("x") - assert str(sc) == """.. _x: + content = SphinxContent() + content.add_label("x") + assert str(content) == """.. _x: """ def test_directive(): - sc = SphinxContent() - with sc.directive("x"): - sc.add("y") - assert str(sc) == """.. x:: + content = SphinxContent() + with content.directive("x"): + content.add("y") + assert str(content) == """.. x:: y """ - with sc.directive("z", "xy", [":a:", ":b:"]): - sc.add("c") - assert str(sc) == """.. x:: + with content.directive("z", "xy", [":a:", ":b:"]): + content.add("c") + assert str(content) == """.. x:: y @@ -61,13 +61,13 @@ def test_directive(): def test_add_header(): - sc = SphinxContent() - sc.add_header("x") - assert str(sc) == """x + content = SphinxContent() + content.add_header("x") + assert str(content) == """x = """ - sc.add_header("yz", 1) - assert str(sc) == """x + content.add_header("yz", 1) + assert str(content) == """x = yz @@ -76,17 +76,17 @@ yz def test_add_header_with_label(): - sc = SphinxContent() - label = sc.add_header_with_label("x", 1) + content = SphinxContent() + label = content.add_header_with_label("x", 1) assert label == "SectionX" - assert str(sc) == """.. _SectionX: + assert str(content) == """.. _SectionX: x * """ - label = sc.add_header_with_label("yz w", 2) + label = content.add_header_with_label("yz w", 2) assert label == "SectionYzW" - assert str(sc) == """.. _SectionX: + assert str(content) == """.. _SectionX: x * @@ -171,30 +171,30 @@ d def test_append(): - sc = SphinxContent() - sc.append("x") - assert str(sc) == """x + content = SphinxContent() + content.append("x") + assert str(content) == """x """ - with sc.indent(): - sc.append("y") - assert str(sc) == """x + with content.indent(): + content.append("y") + assert str(content) == """x y """ - sc.append("") - assert str(sc) == """x + content.append("") + assert str(content) == """x y """ def test_add_index_entries(): - sc = SphinxContent() - sc.add_index_entries(["x", "y"]) - assert str(sc) == """.. index:: x + content = SphinxContent() + content.add_index_entries(["x", "y"]) + assert str(content) == """.. index:: x .. index:: y """ - sc.add_index_entries("z") - assert str(sc) == """.. index:: x + content.add_index_entries("z") + assert str(content) == """.. index:: x .. index:: y .. index:: z @@ -202,39 +202,39 @@ def test_add_index_entries(): def test_add_definition_item(): - sc = SphinxContent() - sc.add_definition_item("x", ["y", "z"]) - assert str(sc) == """x + content = SphinxContent() + content.add_definition_item("x", ["y", "z"]) + assert str(content) == """x y z """ - sc = SphinxContent() - sc.add_definition_item("a", "\n b\n") - assert str(sc) == """a + content = SphinxContent() + content.add_definition_item("a", "\n b\n") + assert str(content) == """a b """ def test_license(): - sc = SphinxContent() + content = SphinxContent() with pytest.raises(ValueError): - sc.register_license("x") - sc.register_license("CC-BY-SA-4.0") - assert str(sc) == "" - sc.add_licence_and_copyrights() - assert str(sc) == """.. SPDX-License-Identifier: CC-BY-SA-4.0 + content.register_license("x") + content.register_license("CC-BY-SA-4.0") + assert str(content) == "" + content.add_licence_and_copyrights() + assert str(content) == """.. SPDX-License-Identifier: CC-BY-SA-4.0 """ def test_license_and_copyrights(): - sc = SphinxContent() + content = SphinxContent() with pytest.raises(ValueError): - sc.register_license("x") - sc.register_copyright("Copyright (C) A") - assert str(sc) == "" - sc.add_licence_and_copyrights() - assert str(sc) == """.. SPDX-License-Identifier: CC-BY-SA-4.0 + content.register_license("x") + content.register_copyright("Copyright (C) A") + assert str(content) == "" + content.add_licence_and_copyrights() + assert str(content) == """.. SPDX-License-Identifier: CC-BY-SA-4.0 .. Copyright (C) A |