From 27c788bda1624a665830d34b4cc7c9337ca1ce2b Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Sun, 13 Dec 2020 17:29:48 +0100 Subject: 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 37966417..cc46338a 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 93afb8bf..a45f4fc1 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 289b6a6e..3663acba 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, -- cgit v1.2.3