[rtems-central commit] items: Simplify ItemMapper

Sebastian Huber sebh at rtems.org
Mon Dec 14 11:05:09 UTC 2020


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

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Sun Dec 13 17:29:48 2020 +0100

items: Simplify ItemMapper

---

 rtemsspec/items.py                      | 74 ++++++++++++++++++---------------
 rtemsspec/tests/test_items_itemcache.py | 10 ++---
 rtemsspec/validation.py                 | 11 ++---
 3 files changed, 49 insertions(+), 46 deletions(-)

diff --git a/rtemsspec/items.py b/rtemsspec/items.py
index 3796641..cc46338 100644
--- a/rtemsspec/items.py
+++ b/rtemsspec/items.py
@@ -31,7 +31,7 @@ import pickle
 import string
 import stat
 from typing import Any, Callable, Dict, Iterable, Iterator, List, NamedTuple, \
-    Mapping, Optional, Tuple, Union
+    Optional, Tuple, Union
 import yaml
 
 
@@ -393,7 +393,26 @@ class ItemTemplate(string.Template):
     idpattern = "[a-zA-Z0-9._/-]+(:[][a-zA-Z0-9._/-]+)?(|[a-zA-Z0-9_]+)*"
 
 
-class ItemMapper(Mapping[str, object]):
+class _ItemMapperContext(dict):
+    """ Context to map identifiers to items and attribute values. """
+    def __init__(self, mapper: "ItemMapper", item: Optional[Item],
+                 prefix: Optional[str], recursive: bool):
+        super().__init__()
+        self._mapper = mapper
+        self._item = item
+        self._prefix = prefix
+        self._recursive = recursive
+
+    def __getitem__(self, identifier):
+        item, key_path, value = self._mapper.map(identifier, self._item,
+                                                 self._prefix)
+        if self._recursive:
+            return self._mapper.substitute(value, item,
+                                           os.path.dirname(key_path))
+        return value
+
+
+class ItemMapper:
     """ Maps identifiers to items and attribute values. """
     def __init__(self, item: Item, recursive: bool = False):
         self._item = item
@@ -442,9 +461,13 @@ class ItemMapper(Mapping[str, object]):
         """ Returns the get value map for the item. """
         return self._get_value_map.get(item.type, {})
 
-    def map(self, identifier: str) -> Tuple[Item, str, Any]:
+    def map(self,
+            identifier: str,
+            item: Optional[Item] = None,
+            prefix: Optional[str] = None) -> Tuple[Item, str, Any]:
         """
-        Maps an identifier to the corresponding item and attribute value.
+        Maps an identifier with item and prefix to the corresponding item and
+        attribute value.
         """
         uid_key_path, *pipes = identifier.split("|")
         colon = uid_key_path.find(":")
@@ -453,8 +476,10 @@ class ItemMapper(Mapping[str, object]):
         else:
             uid, key_path = uid_key_path, "/_uid"
         if uid == ".":
-            item = self._item
-            prefix = "/".join(self._prefix)
+            if item is None:
+                item = self._item
+            if prefix is None:
+                prefix = "/".join(self._prefix)
         else:
             item = self._item.map(uid)
             prefix = ""
@@ -465,43 +490,24 @@ class ItemMapper(Mapping[str, object]):
             value = getattr(self, func)(value)
         return item, key_path, value
 
-    @contextmanager
-    def _item_and_prefix(self, item: Item, prefix: str) -> Iterator[None]:
-        item_2 = self._item
-        prefix_2 = self._prefix
-        self._item = item
-        self._prefix = [prefix]
-        yield
-        self._item = item_2
-        self._prefix = prefix_2
-
     def __getitem__(self, identifier):
         item, key_path, value = self.map(identifier)
         if self._recursive:
-            with self._item_and_prefix(item, os.path.dirname(key_path)):
-                return self.substitute(value)
+            return self.substitute(value, item, os.path.dirname(key_path))
         return value
 
-    def __iter__(self):
-        raise StopIteration
-
-    def __len__(self):
-        raise AttributeError
-
-    def substitute(self, text: Optional[str]) -> str:
-        """ Performs a variable substitution using the item mapper. """
-        if not text:
-            return ""
-        return ItemTemplate(text).substitute(self)
-
-    def substitute_with_prefix(self, text: Optional[str], prefix: str) -> str:
+    def substitute(self,
+                   text: Optional[str],
+                   item: Optional[Item] = None,
+                   prefix: Optional[str] = None) -> str:
         """
-        Performs a variable substitution using the item mapper with a prefix.
+        Performs a variable substitution using the item mapper with the item
+        and prefix.
         """
         if not text:
             return ""
-        with self.prefix(prefix):
-            return ItemTemplate(text).substitute(self)
+        context = _ItemMapperContext(self, item, prefix, self._recursive)
+        return ItemTemplate(text).substitute(context)
 
 
 class _SpecType(NamedTuple):
diff --git a/rtemsspec/tests/test_items_itemcache.py b/rtemsspec/tests/test_items_itemcache.py
index 93afb8b..a45f4fc 100644
--- a/rtemsspec/tests/test_items_itemcache.py
+++ b/rtemsspec/tests/test_items_itemcache.py
@@ -128,7 +128,7 @@ def test_item_mapper(tmpdir):
     assert base_mapper["d/c:v"] == "c"
     mapper = Mapper(item)
     assert mapper.substitute(None) == ""
-    assert mapper.substitute_with_prefix(None, "v") == ""
+    assert mapper.substitute(None, prefix="v") == ""
     with mapper.prefix("v"):
         assert mapper[".:."] == "p"
         assert mapper[".:../x/y"] == "z"
@@ -137,7 +137,7 @@ def test_item_mapper(tmpdir):
         assert key_path_2 == "/v"
         assert value_2 == "p"
         assert mapper.substitute("$$${.:.}") == "$p"
-    assert mapper.substitute_with_prefix("$$${.:.}", "v") == "$p"
+    assert mapper.substitute("$$${.:.}", prefix="v") == "$p"
     with mapper.prefix("x"):
         with mapper.prefix("y"):
             assert mapper[".:."] == "z"
@@ -154,13 +154,9 @@ def test_item_mapper(tmpdir):
     assert item_3 == item
     assert key_path_3 == "/v"
     assert value_3 == "p"
-    with pytest.raises(StopIteration):
-        for something in mapper:
-            pass
-    with pytest.raises(AttributeError):
-        len(mapper)
     recursive_mapper = ItemMapper(item, recursive=True)
     assert recursive_mapper.substitute("${.:/r1/r2/r3}") == "foobar"
+    assert recursive_mapper[".:/r1/r2/r3"] == "foobar"
 
 
 def test_empty_item_mapper():
diff --git a/rtemsspec/validation.py b/rtemsspec/validation.py
index 289b6a6..3663acb 100644
--- a/rtemsspec/validation.py
+++ b/rtemsspec/validation.py
@@ -62,7 +62,10 @@ class _Mapper(ItemMapper):
         """ Resets the test step counter. """
         self._step = 0
 
-    def map(self, identifier: str) -> Tuple[Item, str, Any]:
+    def map(self,
+            identifier: str,
+            item: Optional[Item] = None,
+            prefix: Optional[str] = None) -> Tuple[Item, str, Any]:
         if identifier == "step":
             step = self._step
             self._step = step + 1
@@ -72,7 +75,7 @@ class _Mapper(ItemMapper):
             inc = int(match.group(1))
             self._step += inc
             return self._item, "step", f"Accounts for {inc} test plan steps"
-        return super().map(identifier)
+        return super().map(identifier, item, prefix)
 
 
 def _add_ingroup(content: CContent, items: List["_TestItem"]) -> None:
@@ -151,9 +154,7 @@ class _TestItem:
         """
         Performs a variable substitution for text with an optional prefix.
         """
-        if prefix:
-            return self._mapper.substitute_with_prefix(text, prefix)
-        return self._mapper.substitute(text)
+        return self._mapper.substitute(text, prefix=prefix)
 
     def add_test_case_description(
             self, content: CContent,



More information about the vc mailing list