source: rtems-central/rtemsqual/items.py @ a4e08c5

Last change on this file since a4e08c5 was eb54ec9, checked in by Sebastian Huber <sebastian.huber@…>, on 04/16/20 at 19:00:23

Remove pylint: disable=useless-object-inheritance

  • Property mode set to 100644
File size: 7.4 KB
Line 
1# SPDX-License-Identifier: BSD-2-Clause
2""" This module provides specification items and an item cache. """
3
4# Copyright (C) 2019, 2020 embedded brains GmbH (http://www.embedded-brains.de)
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26
27import os
28import pickle
29import stat
30from typing import Any, List, Dict
31import yaml
32
33from rtemsqual.content import SphinxContent
34
35ItemList = List["Item"]
36ItemMap = Dict[str, "Item"]
37
38
39def _is_enabled_op_and(enabled: List[str], enabled_by: Any) -> bool:
40    for next_enabled_by in enabled_by:
41        if not _is_enabled(enabled, next_enabled_by):
42            return False
43    return True
44
45
46def _is_enabled_op_false(_enabled: List[str], _enabled_by: Any) -> bool:
47    return False
48
49
50def _is_enabled_op_not(enabled: List[str], enabled_by: Any) -> bool:
51    return not _is_enabled(enabled, enabled_by)
52
53
54def _is_enabled_op_or(enabled: List[str], enabled_by: Any) -> bool:
55    for next_enabled_by in enabled_by:
56        if _is_enabled(enabled, next_enabled_by):
57            return True
58    return False
59
60
61_IS_ENABLED_OP = {
62    "and": _is_enabled_op_and,
63    "not": _is_enabled_op_not,
64    "or": _is_enabled_op_or
65}
66
67
68def _is_enabled(enabled: List[str], enabled_by: Any) -> bool:
69    if enabled_by:
70        if isinstance(enabled_by, list):
71            return _is_enabled_op_or(enabled, enabled_by)
72        if isinstance(enabled_by, dict):
73            if len(enabled_by) == 1:
74                key = next(iter(enabled_by))
75                return _IS_ENABLED_OP.get(key, _is_enabled_op_false)(
76                    enabled, enabled_by[key])
77            return False
78        return enabled_by in enabled
79    return True
80
81
82class Item:
83    """ Objects of this class represent a specification item. """
84    def __init__(self, uid: str, data: Any):
85        self._uid = uid
86        self._data = data
87        self._links = []  # type: ItemList
88        self._children = []  # type: ItemList
89
90    def __contains__(self, key: str) -> bool:
91        return key in self._data
92
93    def __getitem__(self, name: str) -> Any:
94        return self._data[name]
95
96    @property
97    def uid(self) -> str:
98        """ Returns the UID of the item. """
99        return self._uid
100
101    @property
102    def parents(self) -> ItemList:
103        """ Returns the list of parents of this items. """
104        return self._links
105
106    @property
107    def children(self) -> ItemList:
108        """ Returns the list of children of this items. """
109        return self._children
110
111    def init_parents(self, item_cache: "ItemCache"):
112        """ Initializes the list of parents of this items. """
113        for link in self._data["links"]:
114            self._links.append(item_cache[list(link.keys())[0]])
115
116    def add_child(self, child: "Item"):
117        """ Adds a child to this item. """
118        self._children.append(child)
119
120    def register_license_and_copyrights(self, content: SphinxContent):
121        """ Registers the license and copyrights of this item. """
122        content.register_license(self["SPDX-License-Identifier"])
123        for statement in self["copyrights"]:
124            content.register_copyright(statement)
125
126    def is_enabled(self, enabled: List[str]):
127        """ Returns true if the item is enabled by the specified enables. """
128        return _is_enabled(enabled, self["enabled-by"])
129
130
131class ItemCache:
132    """ This class provides a cache of specification items. """
133    def __init__(self, config: Any):
134        self._items = {}  # type: ItemMap
135        self._top_level = {}  # type: ItemMap
136        self._load_items(config)
137
138    def __getitem__(self, uid: str) -> Item:
139        return self._items[uid]
140
141    @property
142    def all(self) -> ItemMap:
143        """ Returns the map of all specification items. """
144        return self._items
145
146    @property
147    def top_level(self) -> ItemMap:
148        """ Returns the map of top-level specification items. """
149        return self._top_level
150
151    def _load_items_in_dir(self, path: str, cache_file: str,
152                           update_cache: bool) -> None:
153        data_by_uid = {}  # type: Dict[str, Any]
154        if update_cache:
155            for name in os.listdir(path):
156                path2 = os.path.join(path, name)
157                if name.endswith(".yml") and not name.startswith("."):
158                    uid = os.path.basename(name).replace(".yml", "")
159                    with open(path2, "r") as yaml_src:
160                        data_by_uid[uid] = yaml.safe_load(yaml_src.read())
161            os.makedirs(os.path.dirname(cache_file), exist_ok=True)
162            with open(cache_file, "wb") as out:
163                pickle.dump(data_by_uid, out)
164        else:
165            with open(cache_file, "rb") as pickle_src:
166                data_by_uid = pickle.load(pickle_src)
167        for uid, data in data_by_uid.items():
168            item = Item(uid, data)
169            self._items[uid] = item
170            if not item["links"]:
171                self._top_level[uid] = item
172
173    def _load_items_recursive(self, path: str, cache_dir: str) -> None:
174        mid = os.path.abspath(path)
175        mid = mid.replace(os.path.commonprefix([cache_dir, mid]), "")
176        cache_file = os.path.join(cache_dir, mid, "spec.pickle")
177        try:
178            mtime = os.path.getmtime(cache_file)
179            update_cache = False
180        except FileNotFoundError:
181            update_cache = True
182        for name in os.listdir(path):
183            path2 = os.path.join(path, name)
184            if name.endswith(".yml") and not name.startswith("."):
185                update_cache = update_cache or mtime <= os.path.getmtime(path2)
186            else:
187                if stat.S_ISDIR(os.lstat(path2).st_mode):
188                    self._load_items_recursive(path2, cache_dir)
189        self._load_items_in_dir(path, cache_file, update_cache)
190
191    def _init_parents(self) -> None:
192        for item in self._items.values():
193            item.init_parents(self)
194
195    def _init_children(self) -> None:
196        for item in self._items.values():
197            for parent in item.parents:
198                parent.add_child(item)
199
200    def _load_items(self, config: Any) -> None:
201        cache_dir = os.path.abspath(config["cache-directory"])
202        for path in config["paths"]:
203            self._load_items_recursive(path, cache_dir)
204        self._init_parents()
205        self._init_children()
Note: See TracBrowser for help on using the repository browser.