[rtems-central commit] glossary: Support hierarchical glossary terms

Sebastian Huber sebh at rtems.org
Tue May 9 13:45:26 UTC 2023


Module:    rtems-central
Branch:    master
Commit:    6b77b76816a839ad6e9ebb0c9bb9327dcf406ac9
Changeset: http://git.rtems.org/rtems-central/commit/?id=6b77b76816a839ad6e9ebb0c9bb9327dcf406ac9

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Fri May  5 14:41:19 2023 +0200

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(-)

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 <Not so General - 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)
 



More information about the vc mailing list