Changeset 22ec40f in rtems-central


Ignore:
Timestamp:
Apr 30, 2020, 5:14:44 AM (6 months ago)
Author:
Sebastian Huber <sebastian.huber@…>
Branches:
master
Children:
520ba1d
Parents:
3b9b2bd
git-author:
Sebastian Huber <sebastian.huber@…> (04/30/20 05:14:44)
git-committer:
Sebastian Huber <sebastian.huber@…> (05/28/20 08:34:46)
Message:

items: Add ItemMapper? and ItemTemplate? classes

Location:
rtemsqual
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • rtemsqual/items.py

    r3b9b2bd r22ec40f  
    2525# POSSIBILITY OF SUCH DAMAGE.
    2626
     27from contextlib import contextmanager
    2728import os
    2829import pickle
     30import string
    2931import stat
    30 from typing import Any, Dict, Iterator, List
     32from typing import Any, Callable, Dict, Iterator, List, Mapping, Tuple
    3133import yaml
    3234
     
    3436
    3537ItemMap = Dict[str, "Item"]
     38KeyMapper = Callable[["Item", Any, str], Any]
    3639
    3740
     
    108111
    109112
     113def _key_error(_item: "Item", _value: Any, _key: str) -> str:
     114    raise KeyError
     115
     116
    110117class Item:
    111118    """ Objects of this class represent a specification item. """
     
    123130        return self._data[name]
    124131
     132    def get(self,
     133            key_path: str,
     134            prefix: str = "",
     135            mapper: KeyMapper = _key_error) -> Any:
     136        """ Gets the attribute value corresponding to the key path. """
     137        if not os.path.isabs(key_path):
     138            key_path = os.path.join(prefix, key_path)
     139        key_path = os.path.normpath(key_path)
     140        value = self._data
     141        for key in key_path.strip("/").split("/"):
     142            parts = key.split("[")
     143            try:
     144                value = value[parts[0]]
     145            except KeyError:
     146                value = mapper(self, value, parts[0])
     147            try:
     148                value = value[int(parts[1].split("]")[0])]
     149            except IndexError:
     150                pass
     151        return value
     152
    125153    @property
    126154    def uid(self) -> str:
     
    133161        item.
    134162        """
     163        if abs_or_rel_uid == ".":
     164            return self._uid
    135165        if os.path.isabs(abs_or_rel_uid):
    136166            return abs_or_rel_uid
     
    207237            self._data = yaml.safe_load(src.read())
    208238            self._data["_file"] = filename
     239
     240
     241class ItemTemplate(string.Template):
     242    """ String template for item mapper identifiers. """
     243    idpattern = "[a-zA-Z0-9._/-]+:[][a-zA-Z0-9._/-]+(|[a-zA-Z0-9_]+)*"
     244
     245
     246class ItemMapper(Mapping[str, object]):
     247    """ Maps identifiers to items and attribute values. """
     248    def __init__(self, item: "Item"):
     249        self._item = item
     250        self._prefix = [""]
     251
     252    def push_prefix(self, prefix: str) -> None:
     253        """ Pushes a key path prefix. """
     254        self._prefix.append(prefix)
     255
     256    def pop_prefix(self) -> None:
     257        """ Pops a key path prefix. """
     258        self._prefix.pop()
     259
     260    @contextmanager
     261    def prefix(self, prefix: str) -> Iterator[None]:
     262        """ Opens a key path prefix context. """
     263        self.push_prefix(prefix)
     264        yield
     265        self.pop_prefix()
     266
     267    def map(self, identifier: str) -> Tuple[Item, Any]:
     268        """
     269        Maps an identifier to the corresponding item and attribute value.
     270        """
     271        parts = identifier.split("|")
     272        uid, key_path = parts[0].split(":")
     273        if uid == ".":
     274            item = self._item
     275            prefix = "/".join(self._prefix)
     276        else:
     277            item = self._item.map(uid)
     278            prefix = ""
     279        value = item.get(key_path, prefix, self._key_mapper)
     280        for func in parts[1:]:
     281            value = getattr(self, func)(value)
     282        return item, value
     283
     284    def __getitem__(self, identifier):
     285        return self.map(identifier)[1]
     286
     287    def __iter__(self):
     288        raise StopIteration
     289
     290    def __len__(self):
     291        raise AttributeError
     292
     293    def substitute(self, text: str) -> str:
     294        """ Performs a variable substitution using the item mapper. """
     295        return ItemTemplate(text).substitute(self)
     296
     297    def _key_mapper(self, item: Item, value: Any, key: str) -> Any:
     298        return getattr(self, key.replace("-", "_"))(item, value)
    209299
    210300
  • rtemsqual/tests/spec-item-cache/d/c.yml

    r3b9b2bd r22ec40f  
    33  uid: ../p
    44v: c
     5a:
     6  b: e
     7  f:
     8  - 1
     9  - 2
     10  - 3
     11  - g:
     12    - 4
     13    - 5
  • rtemsqual/tests/test_items_item.py

    r3b9b2bd r22ec40f  
    3838def test_to_abs_uid():
    3939    item = Item(EmptyCache(), "/x/y", {})
     40    assert item.to_abs_uid(".") == "/x/y"
    4041    assert item.to_abs_uid("z") == "/x/z"
    4142    assert item.to_abs_uid("/z") == "/z"
     
    5556    assert "x" in item
    5657    assert "a" not in item
     58
     59
     60def test_get():
     61    data = {}
     62    data["a"] = {"b": "c", "d": [1, 2, 3]}
     63    data["x"] = "y"
     64    item = Item(EmptyCache(), "z", data)
     65    assert item.get("x") == "y"
     66    assert item.get("a/d[2]") == 3
     67    assert item.get("a/b/../d[0]") == 1
     68    assert item.get("/a/b/../d[0]") == 1
     69    assert item.get("../d[0]", "a/b") == 1
     70    with pytest.raises(KeyError):
     71        assert item.get("y")
     72    with pytest.raises(KeyError):
     73        assert item.get("[")
     74    with pytest.raises(ValueError):
     75        assert item.get("x[y]")
    5776
    5877
  • rtemsqual/tests/test_items_itemcache.py

    r3b9b2bd r22ec40f  
    2929import shutil
    3030
    31 from rtemsqual.items import ItemCache
     31from rtemsqual.items import ItemCache, ItemMapper, ItemTemplate
    3232
    3333
     
    3737
    3838
    39 def test_load(tmpdir):
     39def _create_spec_and_config(tmpdir):
    4040    config = {}
    4141    cache_dir = os.path.join(tmpdir, "cache")
     
    4545    shutil.copytree(spec_src, spec_dst)
    4646    config["paths"] = [os.path.normpath(spec_dst)]
    47     ic = ItemCache(config)
     47    return config
     48
     49
     50def test_load(tmpdir):
     51    config = _create_spec_and_config(tmpdir)
     52    item_cache = ItemCache(config)
     53    cache_dir = config["cache-directory"]
    4854    assert os.path.exists(os.path.join(cache_dir, "spec", "spec.pickle"))
    4955    assert os.path.exists(os.path.join(cache_dir, "spec", "d", "spec.pickle"))
    50     assert ic["/d/c"]["v"] == "c"
    51     assert ic["/p"]["v"] == "p"
    52     t = ic.top_level
     56    assert item_cache["/d/c"]["v"] == "c"
     57    assert item_cache["/p"]["v"] == "p"
     58    t = item_cache.top_level
    5359    assert len(t) == 1
    5460    p = t["/p"]
     
    5662    assert p.map("/p") == p
    5763    assert p.map("p") == p
    58     a = ic.all
     64    a = item_cache.all
    5965    assert len(a) == 2
    6066    assert a["/p"]["v"] == "p"
    6167    assert a["/d/c"]["v"] == "c"
    62     ic2 = ItemCache(config)
    63     assert ic2["/d/c"]["v"] == "c"
     68    item_cache_2 = ItemCache(config)
     69    assert item_cache_2["/d/c"]["v"] == "c"
    6470    with open(os.path.join(tmpdir, "spec", "d", "c.yml"), "w+") as out:
    6571        out.write("links:\n- role: null\n  uid: ../p\nv: x\n")
    66     ic3 = ItemCache(config)
    67     assert ic3["/d/c"]["v"] == "x"
     72    item_cache_3 = ItemCache(config)
     73    assert item_cache_3["/d/c"]["v"] == "x"
     74
     75
     76class Mapper(ItemMapper):
     77    def __init__(self, item):
     78        super().__init__(item)
     79
     80    def u(self, value):
     81        return "u" + value
     82
     83    def v(self, value):
     84        return "v" + value
     85
     86    def dup(self, value):
     87        return value + value
     88
     89    def x_to_b(self, item, value):
     90        return value["b"]
     91
     92
     93def test_item_mapper(tmpdir):
     94    config = _create_spec_and_config(tmpdir)
     95    item_cache = ItemCache(config)
     96    item = item_cache["/p"]
     97    mapper = Mapper(item)
     98    with mapper.prefix("v"):
     99        assert mapper[".:."] == "p"
     100        assert mapper[".:../x/y"] == "z"
     101        item_2, value_2 = mapper.map(".:.")
     102        assert item == item_2
     103        assert value_2 == "p"
     104        assert mapper.substitute("$$${.:.}") == "$p"
     105    with mapper.prefix("x"):
     106        with mapper.prefix("y"):
     107            assert mapper[".:."] == "z"
     108    assert mapper["d/c:v"] == "c"
     109    assert mapper["d/c:a/b"] == "e"
     110    assert mapper["d/c:a/b|u"] == "ue"
     111    assert mapper["d/c:a/x-to-b|u|v"] == "vue"
     112    assert mapper["d/c:a/f[1]"] == 2
     113    assert mapper["d/c:a/../a/f[3]/g[0]|dup"] == 8
     114    item_3, value_3 = mapper.map("/p:/v")
     115    assert item == item_3
     116    assert value_3 == "p"
     117    with pytest.raises(StopIteration):
     118        for something in mapper:
     119            pass
     120    with pytest.raises(AttributeError):
     121        len(mapper)
Note: See TracChangeset for help on using the changeset viewer.