From 6b77b76816a839ad6e9ebb0c9bb9327dcf406ac9 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 5 May 2023 14:41:19 +0200 Subject: glossary: Support hierarchical glossary terms --- rtemsspec/glossary.py | 61 +++++++++++++----------- rtemsspec/sphinxcontent.py | 8 +++- rtemsspec/tests/spec-glossary/g.yml | 2 +- rtemsspec/tests/spec-glossary/glossary/sub/g.yml | 11 +++++ rtemsspec/tests/spec-glossary/glossary/sub/x.yml | 12 +++++ rtemsspec/tests/spec-glossary/glossary/t.yml | 4 +- rtemsspec/tests/spec-glossary/glossary/u.yml | 4 +- rtemsspec/tests/spec-glossary/glossary/v.yml | 6 +-- rtemsspec/tests/spec-interface/glossary.yml | 9 ++++ rtemsspec/tests/spec-interface/term.yml | 4 +- rtemsspec/tests/spec-sphinx/g.yml | 9 ++++ rtemsspec/tests/spec-sphinx/x.yml | 4 +- rtemsspec/tests/spec-sphinx/z.yml | 4 +- rtemsspec/tests/test_content_sphinx.py | 2 + rtemsspec/tests/test_glossary.py | 18 ++++--- rtemsspec/tests/test_interfacedoc.py | 2 + spec2modules.py | 16 +++++-- 17 files changed, 125 insertions(+), 51 deletions(-) create mode 100644 rtemsspec/tests/spec-glossary/glossary/sub/g.yml create mode 100644 rtemsspec/tests/spec-glossary/glossary/sub/x.yml create mode 100644 rtemsspec/tests/spec-interface/glossary.yml create mode 100644 rtemsspec/tests/spec-sphinx/g.yml diff --git a/rtemsspec/glossary.py b/rtemsspec/glossary.py index 205d9ceb..ba5667d1 100644 --- a/rtemsspec/glossary.py +++ b/rtemsspec/glossary.py @@ -28,7 +28,7 @@ import glob import re from typing import Any, Dict, List, NamedTuple -from rtemsspec.sphinxcontent import SphinxContent, SphinxInterfaceMapper +from rtemsspec.sphinxcontent import SphinxContent from rtemsspec.items import Item, ItemCache, ItemGetValueContext, ItemMapper ItemMap = Dict[str, Item] @@ -40,28 +40,38 @@ class _Glossary(NamedTuple): term_to_item: ItemMap +def augment_glossary_terms(item: Item, path: List[str]) -> None: + """ + Augments the glossary term items of the cache with a glossary path prefix. + """ + for child in item.children("requirement-refinement"): + augment_glossary_terms(child, path + [child["name"]]) + for child in item.children("glossary-member"): + term = " - ".join(path + [child["term"]]) + child["_term"] = term + + def _gather_glossary_terms(item: Item, glossary: _Glossary) -> None: - for child in item.children(): + for child in item.children("requirement-refinement"): _gather_glossary_terms(child, glossary) - if item["type"] == "glossary" and item["glossary-type"] == "term": - glossary.uid_to_item[item.uid] = item - term = item["term"] + for child in item.children("glossary-member"): + glossary.uid_to_item[child.uid] = child + term = child["_term"] assert term not in glossary.term_to_item - glossary.term_to_item[term] = item + glossary.term_to_item[term] = child def _generate_glossary_content(terms: ItemMap, header: str, target: str, - group_uids: List[str]) -> None: + mapper: ItemMapper) -> None: content = SphinxContent() content.add_header(header, level=1) content.add(".. glossary::") with content.indent(): content.add(":sorted:") - for item in sorted(terms.values(), key=lambda x: x["term"].lower()): + for item in sorted(terms.values(), key=lambda x: x["_term"].lower()): content.register_license_and_copyrights_of_item(item) - text = SphinxInterfaceMapper(item, - group_uids).substitute(item["text"]) - content.add_definition_item(item["term"], text) + text = mapper.substitute(item["text"], item) + content.add_definition_item(item["_term"], text) content.add_licence_and_copyrights() content.write(target) @@ -109,24 +119,23 @@ def _resolve_glossary_terms(document_terms: ItemMap) -> None: def _generate_project_glossary(glossary: _Glossary, header: str, target: str, - group_uids: List[str]) -> None: + mapper: ItemMapper) -> None: if target: _generate_glossary_content(glossary.uid_to_item, header, target, - group_uids) + mapper) -def _generate_document_glossary(config: dict, group_uids: List[str], - glossary: _Glossary) -> None: +def _generate_document_glossary(config: dict, glossary: _Glossary, + mapper: ItemMapper) -> None: document_terms: ItemMap = {} for path in config["rest-source-paths"]: _find_glossary_terms(path, document_terms, glossary) _resolve_glossary_terms(document_terms) _generate_glossary_content(document_terms, config["header"], - config["target"], group_uids) + config["target"], mapper) -def generate(config: dict, group_uids: List[str], - item_cache: ItemCache) -> None: +def generate(config: dict, item_cache: ItemCache, mapper: ItemMapper) -> None: """ Generates glossaries of terms according to the configuration. @@ -134,18 +143,14 @@ def generate(config: dict, group_uids: List[str], :param item_cache: The specification item cache containing the glossary groups and terms. """ - groups: ItemMap = {} - for uid, item in item_cache.all.items(): - if item.type == "glossary/group": - groups[uid] = item - project_glossary = _Glossary({}, {}) - for group in config["project-groups"]: - _gather_glossary_terms(groups[group], project_glossary) + for uid in config["project-groups"]: + group = item_cache[uid] + assert group.type == "glossary/group" + _gather_glossary_terms(group, project_glossary) _generate_project_glossary(project_glossary, config["project-header"], - config["project-target"], group_uids) + config["project-target"], mapper) for document_config in config["documents"]: - _generate_document_glossary(document_config, group_uids, - project_glossary) + _generate_document_glossary(document_config, project_glossary, mapper) diff --git a/rtemsspec/sphinxcontent.py b/rtemsspec/sphinxcontent.py index c55802ce..18b4ed53 100644 --- a/rtemsspec/sphinxcontent.py +++ b/rtemsspec/sphinxcontent.py @@ -303,12 +303,16 @@ class SphinxContent(Content): def get_value_sphinx_glossary_term(ctx: ItemGetValueContext) -> Any: """ Gets a gossary term. """ - return f":term:`{ctx.value[ctx.key]}`" + term = ctx.value[ctx.key] + term_2 = ctx.item["_term"] + if term == term_2: + return f":term:`{term}`" + return f":term:`{term} <{term_2}>`" def get_value_sphinx_glossary_plural(ctx: ItemGetValueContext) -> Any: """ Gets a gossary term in plural form. """ - return f":term:`{get_value_plural(ctx)} <{ctx.value['term']}>`" + return f":term:`{get_value_plural(ctx)} <{ctx.item['_term']}>`" def _get_appl_config_option(ctx: ItemGetValueContext) -> Any: diff --git a/rtemsspec/tests/spec-glossary/g.yml b/rtemsspec/tests/spec-glossary/g.yml index d1275c12..266b6c81 100644 --- a/rtemsspec/tests/spec-glossary/g.yml +++ b/rtemsspec/tests/spec-glossary/g.yml @@ -1,6 +1,6 @@ SPDX-License-Identifier: CC-BY-SA-4.0 copyrights: -- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +- Copyright (C) 2020 embedded brains GmbH & Co. KG enabled-by: true glossary-type: group links: [] diff --git a/rtemsspec/tests/spec-glossary/glossary/sub/g.yml b/rtemsspec/tests/spec-glossary/glossary/sub/g.yml new file mode 100644 index 00000000..16c9a8bd --- /dev/null +++ b/rtemsspec/tests/spec-glossary/glossary/sub/g.yml @@ -0,0 +1,11 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +enabled-by: true +glossary-type: group +links: +- role: requirement-refinement + uid: ../../g +name: Not so General +text: Text. +type: glossary diff --git a/rtemsspec/tests/spec-glossary/glossary/sub/x.yml b/rtemsspec/tests/spec-glossary/glossary/sub/x.yml new file mode 100644 index 00000000..68b3522b --- /dev/null +++ b/rtemsspec/tests/spec-glossary/glossary/sub/x.yml @@ -0,0 +1,12 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +enabled-by: true +term: x +glossary-type: term +links: +- role: glossary-member + uid: g +text: | + Term text X. +type: glossary diff --git a/rtemsspec/tests/spec-glossary/glossary/t.yml b/rtemsspec/tests/spec-glossary/glossary/t.yml index dd1a9c13..d91f4985 100644 --- a/rtemsspec/tests/spec-glossary/glossary/t.yml +++ b/rtemsspec/tests/spec-glossary/glossary/t.yml @@ -1,11 +1,11 @@ SPDX-License-Identifier: CC-BY-SA-4.0 copyrights: -- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +- Copyright (C) 2020 embedded brains GmbH & Co. KG enabled-by: true term: T glossary-type: term links: -- role: null +- role: glossary-member uid: ../g text: | Term text $$${/glossary/u:/term} ${/glossary/t:/term} diff --git a/rtemsspec/tests/spec-glossary/glossary/u.yml b/rtemsspec/tests/spec-glossary/glossary/u.yml index b1470ae8..8b47af36 100644 --- a/rtemsspec/tests/spec-glossary/glossary/u.yml +++ b/rtemsspec/tests/spec-glossary/glossary/u.yml @@ -1,11 +1,11 @@ SPDX-License-Identifier: CC-BY-SA-4.0 copyrights: -- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +- Copyright (C) 2020 embedded brains GmbH & Co. KG enabled-by: true term: U glossary-type: term links: -- role: null +- role: glossary-member uid: ../g text: | Term text U. diff --git a/rtemsspec/tests/spec-glossary/glossary/v.yml b/rtemsspec/tests/spec-glossary/glossary/v.yml index ba40946f..d39ee644 100644 --- a/rtemsspec/tests/spec-glossary/glossary/v.yml +++ b/rtemsspec/tests/spec-glossary/glossary/v.yml @@ -1,12 +1,12 @@ SPDX-License-Identifier: CC-BY-SA-4.0 copyrights: -- Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +- Copyright (C) 2020 embedded brains GmbH & Co. KG enabled-by: true term: V glossary-type: term links: -- role: null +- role: glossary-member uid: ../g text: | - Term text V. + Term text V. See also ${sub/x:/term}. type: glossary diff --git a/rtemsspec/tests/spec-interface/glossary.yml b/rtemsspec/tests/spec-interface/glossary.yml new file mode 100644 index 00000000..13061700 --- /dev/null +++ b/rtemsspec/tests/spec-interface/glossary.yml @@ -0,0 +1,9 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +enabled-by: true +glossary-type: group +links: [] +name: General +text: Text. +type: glossary diff --git a/rtemsspec/tests/spec-interface/term.yml b/rtemsspec/tests/spec-interface/term.yml index dc49f215..c5b57911 100644 --- a/rtemsspec/tests/spec-interface/term.yml +++ b/rtemsspec/tests/spec-interface/term.yml @@ -1,5 +1,7 @@ enabled-by: true term: x glossary-type: term -links: [] +links: +- role: glossary-member + uid: glossary type: glossary diff --git a/rtemsspec/tests/spec-sphinx/g.yml b/rtemsspec/tests/spec-sphinx/g.yml new file mode 100644 index 00000000..13061700 --- /dev/null +++ b/rtemsspec/tests/spec-sphinx/g.yml @@ -0,0 +1,9 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2023 embedded brains GmbH & Co. KG +enabled-by: true +glossary-type: group +links: [] +name: General +text: Text. +type: glossary diff --git a/rtemsspec/tests/spec-sphinx/x.yml b/rtemsspec/tests/spec-sphinx/x.yml index b18854f6..5ac0270a 100644 --- a/rtemsspec/tests/spec-sphinx/x.yml +++ b/rtemsspec/tests/spec-sphinx/x.yml @@ -1,5 +1,7 @@ enabled-by: true term: y glossary-type: term -links: [] +links: +- role: glossary-member + uid: g type: glossary diff --git a/rtemsspec/tests/spec-sphinx/z.yml b/rtemsspec/tests/spec-sphinx/z.yml index be90f10c..870f1b77 100644 --- a/rtemsspec/tests/spec-sphinx/z.yml +++ b/rtemsspec/tests/spec-sphinx/z.yml @@ -1,5 +1,7 @@ enabled-by: true term: z glossary-type: term -links: [] +links: +- role: glossary-member + uid: g type: glossary diff --git a/rtemsspec/tests/test_content_sphinx.py b/rtemsspec/tests/test_content_sphinx.py index 5979d3c5..4858f796 100644 --- a/rtemsspec/tests/test_content_sphinx.py +++ b/rtemsspec/tests/test_content_sphinx.py @@ -26,6 +26,7 @@ import pytest +from rtemsspec.glossary import augment_glossary_terms from rtemsspec.sphinxcontent import get_reference, make_label, \ SphinxContent, SphinxMapper from rtemsspec.items import Item, ItemCache, ItemMapper @@ -352,6 +353,7 @@ def test_substitute(tmpdir): "spec-sphinx", with_spec_types=True) item_cache = ItemCache(config) + augment_glossary_terms(item_cache["/g"], []) mapper = SphinxMapper(item_cache["/x"]) match = r"substitution for spec:/x using prefix '' failed for text: \${x:/y}" with pytest.raises(ValueError, match=match): diff --git a/rtemsspec/tests/test_glossary.py b/rtemsspec/tests/test_glossary.py index 55b78aa4..f8d2f43c 100644 --- a/rtemsspec/tests/test_glossary.py +++ b/rtemsspec/tests/test_glossary.py @@ -1,7 +1,7 @@ # SPDX-License-Identifier: BSD-2-Clause """ Unit tests for the rtemsspec.glossary module. """ -# Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +# Copyright (C) 2020 embedded brains GmbH & Co. KG # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -26,8 +26,9 @@ import os -from rtemsspec.glossary import generate +from rtemsspec.glossary import augment_glossary_terms, generate from rtemsspec.items import EmptyItemCache, ItemCache +from rtemsspec.sphinxcontent import SphinxMapper from rtemsspec.tests.util import create_item_cache_config_and_copy_spec @@ -56,12 +57,14 @@ def test_glossary(tmpdir): document_glossary = os.path.join(tmpdir, "document", "glossary.rst") doc["target"] = document_glossary glossary_config["documents"] = [doc] - generate(glossary_config, [], item_cache) + glossary_item = item_cache["/g"] + augment_glossary_terms(glossary_item, []) + generate(glossary_config, item_cache, SphinxMapper(glossary_item)) with open(project_glossary, "r") as src: content = """.. SPDX-License-Identifier: CC-BY-SA-4.0 -.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +.. Copyright (C) 2020, 2023 embedded brains GmbH & Co. KG Project Glossary **************** @@ -69,6 +72,9 @@ Project Glossary .. glossary:: :sorted: + Not so General - x + Term text X. + T Term text $:term:`U` :term:`T` term. @@ -77,14 +83,14 @@ Project Glossary Term text U. V - Term text V. + Term text V. See also :term:`x `. """ assert content == src.read() with open(document_glossary, "r") as src: content = """.. SPDX-License-Identifier: CC-BY-SA-4.0 -.. Copyright (C) 2020 embedded brains GmbH (http://www.embedded-brains.de) +.. Copyright (C) 2020 embedded brains GmbH & Co. KG Glossary ******** diff --git a/rtemsspec/tests/test_interfacedoc.py b/rtemsspec/tests/test_interfacedoc.py index 378af748..7ed88ee6 100644 --- a/rtemsspec/tests/test_interfacedoc.py +++ b/rtemsspec/tests/test_interfacedoc.py @@ -27,6 +27,7 @@ import os import pytest +from rtemsspec.glossary import augment_glossary_terms from rtemsspec.interfacedoc import document_directive, generate from rtemsspec.items import EmptyItemCache, ItemCache, ItemMapper from rtemsspec.tests.util import create_item_cache_config_and_copy_spec @@ -51,6 +52,7 @@ def test_interfacedoc(tmpdir): tmpdir, "spec-interface", with_spec_types=True) config = {"enabled": [], "groups": [doc_config, doc_config_2]} item_cache = ItemCache(item_cache_config) + augment_glossary_terms(item_cache["/glossary"], []) generate(config, item_cache) with open(introduction_rst, "r") as src: diff --git a/spec2modules.py b/spec2modules.py index 8c86d396..b2a5e8bb 100755 --- a/spec2modules.py +++ b/spec2modules.py @@ -30,6 +30,8 @@ import difflib import sys import rtemsspec +from rtemsspec.items import EmptyItem +from rtemsspec.sphinxcontent import SphinxInterfaceMapper def _diff(obj: rtemsspec.content.Content, path: str) -> None: @@ -70,18 +72,24 @@ def main() -> None: config = rtemsspec.util.load_config("config.yml") item_cache = rtemsspec.items.ItemCache(config["spec"]) item_cache.set_enabled([], rtemsspec.items.item_is_enabled) + group_uids = [ + doc["group"] for doc in config["interface-documentation"]["groups"] + ] + for uid in config["glossary"]["project-groups"]: + group = item_cache[uid] + assert group.type == "glossary/group" + rtemsspec.glossary.augment_glossary_terms(group, []) rtemsspec.validation.generate(config["validation"], item_cache, args.targets) if not args.targets: - group_uids = [ - doc["group"] for doc in config["interface-documentation"]["groups"] - ] rtemsspec.interface.generate(config["interface"], item_cache) rtemsspec.applconfig.generate(config["appl-config"], group_uids, item_cache) rtemsspec.specdoc.document(config["spec-documentation"], item_cache) - rtemsspec.glossary.generate(config["glossary"], group_uids, item_cache) + rtemsspec.glossary.generate( + config["glossary"], item_cache, + SphinxInterfaceMapper(EmptyItem(), group_uids)) rtemsspec.interfacedoc.generate(config["interface-documentation"], item_cache) -- cgit v1.2.3