[rtems-central commit] items: Add proxy item type

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


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

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

items: Add proxy item type

---

 config.yml                                 |  3 ++-
 rtemsspec/interface.py                     |  5 ++--
 rtemsspec/items.py                         | 43 ++++++++++++++++++++++++++++++
 rtemsspec/tests/spec-item-cache/d/c.yml    |  3 +++
 rtemsspec/tests/spec-item-cache/p.yml      |  5 +++-
 rtemsspec/tests/spec-item-cache/proxy.yml  |  3 +++
 rtemsspec/tests/spec-item-cache/proxy2.yml |  5 ++++
 rtemsspec/tests/spec-item-cache/q.yml      |  6 +++++
 rtemsspec/tests/spec-item-cache/r.yml      |  3 +++
 rtemsspec/tests/spec-item-cache/s.yml      |  6 +++++
 rtemsspec/tests/spec/proxy.yml             | 28 +++++++++++++++++++
 rtemsspec/tests/test_items_itemcache.py    | 20 +++++++++++---
 spec-spec/spec/proxy-member.yml            | 23 ++++++++++++++++
 spec-spec/spec/proxy.yml                   | 28 +++++++++++++++++++
 spec2modules.py                            |  4 +--
 15 files changed, 174 insertions(+), 11 deletions(-)

diff --git a/config.yml b/config.yml
index 16105bf1..1b96b7e7 100644
--- a/config.yml
+++ b/config.yml
@@ -60,12 +60,13 @@ build:
   workspace-directory: workspace/rtems
 spec:
   cache-directory: cache
-  spec-type-root-uid: /spec/root
   paths:
   - spec-spec
   - spec
   - spec-glossary
   - modules/rtems/spec
+  resolve-proxies: true
+  spec-type-root-uid: /spec/root
 glossary:
   project-groups:
   - /glossary-general
diff --git a/rtemsspec/interface.py b/rtemsspec/interface.py
index 85a6781a..d31bcb0e 100644
--- a/rtemsspec/interface.py
+++ b/rtemsspec/interface.py
@@ -871,6 +871,5 @@ def generate(config: dict, item_cache: ItemCache) -> None:
     enabled = config["enabled"]
     enabled_by_defined = _gather_enabled_by_defined(
         config["item-level-interfaces"], item_cache)
-    for item in item_cache.all.values():
-        if item.type == "interface/header-file":
-            _generate_header_file(item, domains, enabled_by_defined, enabled)
+    for item in item_cache.items_by_type.get("interface/header-file", []):
+        _generate_header_file(item, domains, enabled_by_defined, enabled)
diff --git a/rtemsspec/items.py b/rtemsspec/items.py
index d9cf8801..1adbcdc7 100644
--- a/rtemsspec/items.py
+++ b/rtemsspec/items.py
@@ -203,6 +203,7 @@ class Item:
         self._data = data
         self._links_to_parents: List[Link] = []
         self._links_to_children: List[Link] = []
+        self._resolved_proxy = False
 
     def __eq__(self, other: Any) -> bool:
         if not isinstance(other, Item):
@@ -420,6 +421,11 @@ class Item:
         """ Returns true if the item is enabled, otherwise returns false. """
         return self._data["_enabled"]
 
+    @property
+    def resolved_proxy(self) -> bool:
+        """ Is true if the item is a resolved proxy, otherwise false. """
+        return self._resolved_proxy
+
     @property
     def data(self) -> Any:
         """ The item data. """
@@ -707,6 +713,33 @@ def item_is_enabled(_enabled: List[str], _item: Item) -> bool:
     return True
 
 
+def _resolve_proxy(proxy: Item, is_link_enabled: Callable[[Link],
+                                                          bool]) -> None:
+
+    # pylint: disable=protected-access
+    try:
+        member = proxy.child("proxy-member", is_link_enabled=is_link_enabled)
+    except IndexError:
+        pass
+    else:
+        member._links_to_parents.extend(proxy._links_to_parents)
+        member._links_to_children.extend(proxy._links_to_children)
+        proxy._data = member._data
+        proxy._ident = member._ident
+        proxy._resolved_proxy = True
+        proxy._uid = member._uid
+        for link in proxy._links_to_parents:
+            for link_2 in link.item._links_to_children:
+                if link_2.item == proxy:
+                    link_2._item = member
+        for link in proxy._links_to_children:
+            for link_2 in link.item._links_to_parents:
+                if link_2.item == proxy:
+                    link_2._item = member
+        proxy._links_to_children = member._links_to_children
+        proxy._links_to_parents = member._links_to_parents
+
+
 class ItemCache:
     """ This class provides a cache of specification items. """
 
@@ -734,6 +767,8 @@ class ItemCache:
         for item in self._items.values():
             self._set_type(item)
             item["_enabled"] = is_item_enabled(self._enabled, item)
+        if config.get("resolve-proxies", False):
+            self.resolve_proxies()
 
     def __getitem__(self, uid: str) -> Item:
         return self._items[uid]
@@ -774,6 +809,14 @@ class ItemCache:
         for item in self._items.values():
             item["_enabled"] = is_item_enabled(enabled, item)
 
+    def resolve_proxies(
+            self,
+            is_link_enabled: Callable[[Link],
+                                      bool] = _is_link_enabled) -> None:
+        """ Resolves each proxy item to the its first enabled member. """
+        for item in self.items_by_type.get("proxy", []):
+            _resolve_proxy(item, is_link_enabled)
+
     def add_volatile_item(self, uid: str, data: Any) -> Item:
         """
         Adds an item with the specified data to the cache and returns it.
diff --git a/rtemsspec/tests/spec-item-cache/d/c.yml b/rtemsspec/tests/spec-item-cache/d/c.yml
index b31a8978..786b66cd 100644
--- a/rtemsspec/tests/spec-item-cache/d/c.yml
+++ b/rtemsspec/tests/spec-item-cache/d/c.yml
@@ -12,5 +12,8 @@ enabled-by: true
 links:
 - role: null
   uid: ../p
+- role: null
+  uid: ../r
+type: other
 v: c
 r6: ${../p:/r7}
diff --git a/rtemsspec/tests/spec-item-cache/p.yml b/rtemsspec/tests/spec-item-cache/p.yml
index d2d69d33..ca1545e7 100644
--- a/rtemsspec/tests/spec-item-cache/p.yml
+++ b/rtemsspec/tests/spec-item-cache/p.yml
@@ -1,6 +1,9 @@
 enabled-by:
   not: foobar
-links: []
+links:
+- role: proxy-member
+  uid: proxy
+type: other
 v: p
 x:
   y: z
diff --git a/rtemsspec/tests/spec-item-cache/proxy.yml b/rtemsspec/tests/spec-item-cache/proxy.yml
new file mode 100644
index 00000000..5c068b28
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/proxy.yml
@@ -0,0 +1,3 @@
+enabled-by: true
+links: []
+type: proxy
diff --git a/rtemsspec/tests/spec-item-cache/proxy2.yml b/rtemsspec/tests/spec-item-cache/proxy2.yml
new file mode 100644
index 00000000..7507739d
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/proxy2.yml
@@ -0,0 +1,5 @@
+enabled-by: true
+links:
+- role: xyz
+  uid: r
+type: proxy
diff --git a/rtemsspec/tests/spec-item-cache/q.yml b/rtemsspec/tests/spec-item-cache/q.yml
new file mode 100644
index 00000000..c33c3a6b
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/q.yml
@@ -0,0 +1,6 @@
+enabled-by: blub
+links:
+- role: proxy-member
+  uid: proxy
+type: other
+v: q
diff --git a/rtemsspec/tests/spec-item-cache/r.yml b/rtemsspec/tests/spec-item-cache/r.yml
new file mode 100644
index 00000000..2678ce82
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/r.yml
@@ -0,0 +1,3 @@
+enabled-by: true
+links: []
+type: other
diff --git a/rtemsspec/tests/spec-item-cache/s.yml b/rtemsspec/tests/spec-item-cache/s.yml
new file mode 100644
index 00000000..cc19b614
--- /dev/null
+++ b/rtemsspec/tests/spec-item-cache/s.yml
@@ -0,0 +1,6 @@
+enabled-by: foobar
+links:
+- role: proxy-member
+  uid: proxy2
+type: other
+v: s
diff --git a/rtemsspec/tests/spec/proxy.yml b/rtemsspec/tests/spec/proxy.yml
new file mode 100644
index 00000000..f5cbbfb3
--- /dev/null
+++ b/rtemsspec/tests/spec/proxy.yml
@@ -0,0 +1,28 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+  uid: root
+- role: spec-refinement
+  spec-key: type
+  spec-value: proxy
+  uid: root
+spec-description: null
+spec-example: null
+spec-info:
+  dict:
+    attributes: {}
+    description: |
+      Items of similar characteristics may link to a proxy item through links
+      with the ${proxy-member:/spec-name}.  A proxy item resolves to the first
+      member item which is enabled.  Proxies may be used to provide an
+      interface with a common name and implementations which depend on
+      configuration options.  For example, in one configuration a constant
+      could be a compile time constant and in another configuration it could be
+      a read-only object.
+    mandatory-attributes: all
+spec-name: Proxy Item Types
+spec-type: proxy
+type: spec
diff --git a/rtemsspec/tests/test_items_itemcache.py b/rtemsspec/tests/test_items_itemcache.py
index 07cec0bf..ff75db12 100644
--- a/rtemsspec/tests/test_items_itemcache.py
+++ b/rtemsspec/tests/test_items_itemcache.py
@@ -64,7 +64,7 @@ def test_load(tmpdir):
     assert p.map("/p") == p
     assert p.map("p") == p
     a = item_cache.all
-    assert len(a) == 2
+    assert len(a) == 7
     assert a["/p"]["v"] == "p"
     assert a["/d/c"]["v"] == "c"
     item_cache.set_enabled([])
@@ -125,7 +125,11 @@ def get_value_dict(ctx):
 
 
 def test_item_mapper(tmpdir):
-    config = create_item_cache_config_and_copy_spec(tmpdir, "spec-item-cache")
+    config = create_item_cache_config_and_copy_spec(tmpdir,
+                                                    "spec-item-cache",
+                                                    with_spec_types=True)
+    config["enabled"] = ["foobar"]
+    config["resolve-proxies"] = True
     item_cache = ItemCache(config)
     item = item_cache["/p"]
     base_mapper = ItemMapper(item)
@@ -146,10 +150,18 @@ def test_item_mapper(tmpdir):
         with mapper.prefix("y"):
             assert mapper[".:."] == "z"
     assert mapper["."] == "/p"
+    match = r"cannot get value for '/v' of spec:/proxy specified by 'proxy:/v"
+    with pytest.raises(ValueError, match=match):
+        mapper["proxy:/v"]
+    assert not item_cache["/proxy"].resolved_proxy
+    assert item_cache["/proxy2"].resolved_proxy
+    assert not item_cache["/r"].resolved_proxy
+    assert mapper["proxy2:/v"] == "s"
+    assert item_cache["/r"].child("xyz").uid == "/s"
     assert mapper["d/c"] == "/d/c"
     assert mapper["d/c:v"] == "c"
     assert mapper["d/c:a/b"] == "e"
-    mapper.add_get_value(":/a/x-to-b", get_x_to_b_value)
+    mapper.add_get_value("other:/a/x-to-b", get_x_to_b_value)
     assert mapper["d/c:a/x-to-b:args:0:%"] == "eargs:0:%"
     assert mapper["d/c:a/f[1]"] == 2
     assert mapper["d/c:a/../a/f[3]/g[0]"] == 4
@@ -157,7 +169,7 @@ def test_item_mapper(tmpdir):
     assert item_3 == item
     assert key_path_3 == "/v"
     assert value_3 == "p"
-    mapper.add_get_value_dictionary(":/dict", get_value_dict)
+    mapper.add_get_value_dictionary("other:/dict", get_value_dict)
     assert mapper["d/c:/dict/some-arbitrary-key"] == "some-arbitrary-key"
     recursive_mapper = ItemMapper(item, recursive=True)
     assert recursive_mapper.substitute("${.:/r1/r2/r3}") == "foobar"
diff --git a/spec-spec/spec/proxy-member.yml b/spec-spec/spec/proxy-member.yml
new file mode 100644
index 00000000..5265c07c
--- /dev/null
+++ b/spec-spec/spec/proxy-member.yml
@@ -0,0 +1,23 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+  uid: root
+- role: spec-refinement
+  spec-key: role
+  spec-value: proxy-member
+  uid: link
+spec-description: null
+spec-example: null
+spec-info:
+  dict:
+    attributes: {}
+    description: |
+      It defines the proxy member role of links.  Items may use this role to
+      link to ${proxy:/spec-name} items.
+    mandatory-attributes: all
+spec-name: Proxy Member Link Role
+spec-type: proxy-member
+type: spec
diff --git a/spec-spec/spec/proxy.yml b/spec-spec/spec/proxy.yml
new file mode 100644
index 00000000..f5cbbfb3
--- /dev/null
+++ b/spec-spec/spec/proxy.yml
@@ -0,0 +1,28 @@
+SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
+copyrights:
+- Copyright (C) 2023 embedded brains GmbH (http://www.embedded-brains.de)
+enabled-by: true
+links:
+- role: spec-member
+  uid: root
+- role: spec-refinement
+  spec-key: type
+  spec-value: proxy
+  uid: root
+spec-description: null
+spec-example: null
+spec-info:
+  dict:
+    attributes: {}
+    description: |
+      Items of similar characteristics may link to a proxy item through links
+      with the ${proxy-member:/spec-name}.  A proxy item resolves to the first
+      member item which is enabled.  Proxies may be used to provide an
+      interface with a common name and implementations which depend on
+      configuration options.  For example, in one configuration a constant
+      could be a compile time constant and in another configuration it could be
+      a read-only object.
+    mandatory-attributes: all
+spec-name: Proxy Item Types
+spec-type: proxy
+type: spec
diff --git a/spec2modules.py b/spec2modules.py
index 16df8cf8..8c86d396 100755
--- a/spec2modules.py
+++ b/spec2modules.py
@@ -68,8 +68,8 @@ def main() -> None:
     if args.diff:
         rtemsspec.content.Content.write = _diff  # type: ignore
     config = rtemsspec.util.load_config("config.yml")
-    item_cache = rtemsspec.items.ItemCache(
-        config["spec"], is_item_enabled=rtemsspec.items.item_is_enabled)
+    item_cache = rtemsspec.items.ItemCache(config["spec"])
+    item_cache.set_enabled([], rtemsspec.items.item_is_enabled)
     rtemsspec.validation.generate(config["validation"], item_cache,
                                   args.targets)
 



More information about the vc mailing list