Coverage for pyEDAA/Reports/CodeCoverage/UCIS/ToggleBits.py: 77%
122 statements
« prev ^ index » next coverage.py v7.17.3, created at 2026-11-14 02:26 +0000
« prev ^ index » next coverage.py v7.17.3, created at 2026-11-14 02:26 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ____ _ #
3# _ __ _ _| ____| _ \ / \ / \ | _ \ ___ _ __ ___ _ __| |_ ___ #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) / _ \ '_ \ / _ \| '__| __/ __| #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| _ < __/ |_) | (_) | | | |_\__ \ #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| \_\___| .__/ \___/|_| \__|___/ #
7# |_| |___/ |_| #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2026-2026 Electronic Design Automation Abstraction (EDA²) #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32The bits of the toggle coverage of the UCIS XML interchange format: a signal's ``<toggleBit>`` elements and their
33transitions.
34"""
35from __future__ import annotations
37from typing import TYPE_CHECKING, Iterable, Optional as Nullable, Self
39from lxml.etree import _Element
40from pyTooling.Common import getFullyQualifiedName
41from pyTooling.Decorators import export, readonly
42from pyTooling.MetaClasses import ExtendedType
44from pyEDAA.Reports.CodeCoverage.UCIS.Bins import Bin
45from pyEDAA.Reports.CodeCoverage.UCIS.Elements import Base, ObjectAttributesMixin, UserAttribute
47if TYPE_CHECKING: 47 ↛ 48line 47 didn't jump to line 48 because the condition on line 47 was never true
48 from pyEDAA.Reports.CodeCoverage.UCIS.Toggles import ToggleObject
51# A class with a property named like a class - ``Bin`` - can't name that class in the annotation of a field: the class
52# body's namespace, where annotations are evaluated, binds the name to the property.
53_Bin = Bin
56@export
57class ToggleBit(Base, ObjectAttributesMixin):
58 """
59 A ``<toggleBit>`` of a toggle object: a bit of the signal, its indices and its transitions.
60 """
62 _parent: Nullable[ToggleObject] #: The toggle object the bit belongs to.
63 _name: str #: Name of the bit, e.g. ``ff1[2]``.
64 _key: str #: UCIS key of the bit.
65 _indices: list[int] #: The indices of the bit, one per dimension.
66 _toggles: list[Toggle] #: The transitions of the bit.
68 def __init__(
69 self,
70 name: str,
71 key: str,
72 indices: Nullable[Iterable[int]] = None,
73 alias: Nullable[str] = None,
74 excluded: bool = False,
75 excludedReason: Nullable[str] = None,
76 weight: int = 1,
77 userAttributes: Nullable[Iterable[UserAttribute]] = None,
78 *,
79 parent: Nullable[ToggleObject] = None
80 ) -> None:
81 """
82 Initialize the toggle bit, and append it to the bits of its toggle object.
84 Its transitions are added by creating them with this bit as their parent.
86 :param name: Name of the bit, e.g. ``ff1[2]``.
87 :param key: UCIS key of the bit.
88 :param indices: Optional, the indices of the bit, one per dimension. Default: ``None``.
89 :param alias: Optional, an alias of the bit's name. Default: ``None``.
90 :param excluded: Optional, whether the bit is excluded from the coverage. Default: ``False``.
91 :param excludedReason: Optional, why the bit is excluded. Default: ``None``.
92 :param weight: Optional, weight of the bit in computing the coverage. Default: ``1``.
93 :param userAttributes: Optional, the user-defined attributes. Default: ``None``.
94 :param parent: Optional, the toggle object the bit belongs to. Default: ``None``.
95 :raises ValueError: If parameter ``name`` is ``None``.
96 :raises TypeError: If parameter ``name`` isn't of type :class:`str`.
97 :raises ValueError: If parameter ``key`` is ``None``.
98 :raises TypeError: If parameter ``key`` isn't of type :class:`str`.
99 :raises TypeError: If parameter ``indices`` isn't iterable.
100 :raises TypeError: If parameter ``indices`` contains an element not of type :class:`int`.
101 :raises ValueError: If parameter ``indices`` contains a negative index.
102 :raises TypeError: If parameter ``alias`` isn't of type :class:`str`.
103 :raises ValueError: If parameter ``excluded`` is ``None``.
104 :raises TypeError: If parameter ``excluded`` isn't of type :class:`bool`.
105 :raises TypeError: If parameter ``excludedReason`` isn't of type :class:`str`.
106 :raises ValueError: If parameter ``weight`` is ``None``.
107 :raises TypeError: If parameter ``weight`` isn't of type :class:`int`.
108 :raises ValueError: If parameter ``weight`` is negative.
109 :raises TypeError: If parameter ``userAttributes`` isn't iterable.
110 :raises TypeError: If parameter ``userAttributes`` contains an element not of type
111 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`.
112 :raises TypeError: If parameter ``parent`` isn't of type
113 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Toggles.ToggleObject`.
114 """
115 from pyEDAA.Reports.CodeCoverage.UCIS.Toggles import ToggleObject
117 super().__init__(userAttributes)
118 ObjectAttributesMixin.__init__(self, alias, excluded, excludedReason, weight)
120 for parameterName, value in (
121 ("name", name),
122 ("key", key)
123 ):
124 if value is None: 124 ↛ 125line 124 didn't jump to line 125 because the condition on line 124 was never true
125 raise ValueError(f"Parameter '{parameterName}' is None.")
126 elif not isinstance(value, str): 126 ↛ 127line 126 didn't jump to line 127 because the condition on line 126 was never true
127 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.")
128 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
129 raise ex
131 if parent is not None and not isinstance(parent, ToggleObject): 131 ↛ 132line 131 didn't jump to line 132 because the condition on line 131 was never true
132 ex = TypeError("Parameter 'parent' is not of type 'ToggleObject'.")
133 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.")
134 raise ex
136 self._parent = parent
137 self._name = name
138 self._key = key
139 self._indices = indexList = []
140 self._toggles = []
142 if indices is not None: 142 ↛ 160line 142 didn't jump to line 160 because the condition on line 142 was always true
143 if not isinstance(indices, Iterable): 143 ↛ 144line 143 didn't jump to line 144 because the condition on line 143 was never true
144 ex = TypeError("Parameter 'indices' is not iterable.")
145 ex.add_note(f"Got type '{getFullyQualifiedName(indices)}'.")
146 raise ex
148 for index in indices:
149 if not isinstance(index, int) or isinstance(index, bool): 149 ↛ 150line 149 didn't jump to line 150 because the condition on line 149 was never true
150 ex = TypeError("Parameter 'indices' contains an element not of type 'int'.")
151 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
152 raise ex
153 elif index < 0:
154 ex = ValueError("Parameter 'indices' contains a negative index.")
155 ex.add_note(f"Got value '{index}'.")
156 raise ex
158 indexList.append(index)
160 if parent is not None: 160 ↛ exitline 160 didn't return from function '__init__' because the condition on line 160 was always true
161 parent._bits.append(self)
163 @classmethod
164 def Parse(cls, element: _Element, *, parent: Nullable[ToggleObject] = None) -> Self:
165 """
166 Parse a toggle bit and its transitions from its ``<toggleBit>`` element.
168 :param element: The ``<toggleBit>`` element.
169 :param parent: Optional, the toggle object the bit belongs to. Default: ``None``.
170 :returns: The toggle bit.
171 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states.
172 """
173 toggleBit = cls(
174 element.attrib["name"],
175 element.attrib["key"],
176 [int(indexElement.text) for indexElement in element.iterfind("{*}index")],
177 element.attrib.get("alias"),
178 cls._ParseBoolean(element.attrib.get("excluded", "false")),
179 element.attrib.get("excludedReason"),
180 int(element.attrib.get("weight", "1")),
181 cls._ParseUserAttributes(element),
182 parent=parent
183 )
185 for toggleElement in element.iterfind("{*}toggle"):
186 Toggle.Parse(toggleElement, parent=toggleBit)
188 return toggleBit
190 @readonly
191 def Parent(self) -> Nullable[ToggleObject]:
192 """
193 Read-only property to access the toggle object the bit belongs to (:attr:`_parent`).
195 :returns: The toggle object; ``None``, if the bit belongs to none.
196 """
197 return self._parent
199 @readonly
200 def Name(self) -> str:
201 """
202 Read-only property to access the name of the bit (:attr:`_name`).
204 :returns: The name, e.g. ``ff1[2]``.
205 """
206 return self._name
208 @readonly
209 def Key(self) -> str:
210 """
211 Read-only property to access the UCIS key of the bit (:attr:`_key`).
213 :returns: The key, as stated.
214 """
215 return self._key
217 @readonly
218 def Indices(self) -> list[int]:
219 """
220 Read-only property to access the indices of the bit (:attr:`_indices`).
222 :returns: The indices, one per dimension; empty, if the report states none.
223 """
224 return self._indices
226 @readonly
227 def Toggles(self) -> list[Toggle]:
228 """
229 Read-only property to access the transitions of the bit (:attr:`_toggles`).
231 :returns: The transitions, in the order the report lists them.
232 """
233 return self._toggles
236@export
237class Toggle(metaclass=ExtendedType, slots=True):
238 """
239 A ``<toggle>`` of a toggle bit: a transition from one value to another, and its bin - how often it happened.
240 """
242 _parent: Nullable[ToggleBit] #: The toggle bit the transition belongs to.
243 _from: str #: The value before the transition, e.g. ``0``.
244 _to: str #: The value after the transition, e.g. ``1``.
245 _bin: _Bin #: How often the transition happened.
247 def __init__(self, fromValue: str, toValue: str, coverBin: Bin, *, parent: Nullable[ToggleBit] = None) -> None:
248 """
249 Initialize the transition, become its bin's parent, and append it to the transitions of its toggle bit.
251 :param fromValue: The value before the transition, e.g. ``0``.
252 :param toValue: The value after the transition, e.g. ``1``.
253 :param coverBin: How often the transition happened.
254 :param parent: Optional, the toggle bit the transition belongs to. Default: ``None``.
255 :raises ValueError: If parameter ``fromValue`` is ``None``.
256 :raises TypeError: If parameter ``fromValue`` isn't of type :class:`str`.
257 :raises ValueError: If parameter ``toValue`` is ``None``.
258 :raises TypeError: If parameter ``toValue`` isn't of type :class:`str`.
259 :raises ValueError: If parameter ``coverBin`` is ``None``.
260 :raises TypeError: If parameter ``coverBin`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.Bin`.
261 :raises TypeError: If parameter ``parent`` isn't of type :class:`ToggleBit`.
262 """
263 for parameterName, value in (
264 ("fromValue", fromValue),
265 ("toValue", toValue)
266 ):
267 if value is None: 267 ↛ 268line 267 didn't jump to line 268 because the condition on line 267 was never true
268 raise ValueError(f"Parameter '{parameterName}' is None.")
269 elif not isinstance(value, str): 269 ↛ 270line 269 didn't jump to line 270 because the condition on line 269 was never true
270 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.")
271 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
272 raise ex
274 if coverBin is None: 274 ↛ 275line 274 didn't jump to line 275 because the condition on line 274 was never true
275 raise ValueError("Parameter 'coverBin' is None.")
276 elif not isinstance(coverBin, Bin): 276 ↛ 277line 276 didn't jump to line 277 because the condition on line 276 was never true
277 ex = TypeError("Parameter 'coverBin' is not of type 'Bin'.")
278 ex.add_note(f"Got type '{getFullyQualifiedName(coverBin)}'.")
279 raise ex
281 if parent is not None and not isinstance(parent, ToggleBit):
282 ex = TypeError("Parameter 'parent' is not of type 'ToggleBit'.")
283 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.")
284 raise ex
286 self._parent = parent
287 self._from = fromValue
288 self._to = toValue
289 self._bin = coverBin
290 coverBin._parent = self
292 if parent is not None: 292 ↛ exitline 292 didn't return from function '__init__' because the condition on line 292 was always true
293 parent._toggles.append(self)
295 @classmethod
296 def Parse(cls, element: _Element, *, parent: Nullable[ToggleBit] = None) -> Self:
297 """
298 Parse a transition and its bin from its ``<toggle>`` element.
300 :param element: The ``<toggle>`` element.
301 :param parent: Optional, the toggle bit the transition belongs to. Default: ``None``.
302 :returns: The transition.
303 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states.
304 """
305 return cls(element.attrib["from"], element.attrib["to"], Bin.Parse(element.find("{*}bin")), parent=parent)
307 @readonly
308 def Parent(self) -> Nullable[ToggleBit]:
309 """
310 Read-only property to access the toggle bit the transition belongs to (:attr:`_parent`).
312 :returns: The toggle bit; ``None``, if the transition belongs to none.
313 """
314 return self._parent
316 @readonly
317 def From(self) -> str:
318 """
319 Read-only property to access the value before the transition (:attr:`_from`).
321 :returns: The value, as stated, e.g. ``0``.
322 """
323 return self._from
325 @readonly
326 def To(self) -> str:
327 """
328 Read-only property to access the value after the transition (:attr:`_to`).
330 :returns: The value, as stated, e.g. ``1``.
331 """
332 return self._to
334 @readonly
335 def Bin(self) -> Bin:
336 """
337 Read-only property to access the transition's bin (:attr:`_bin`).
339 :returns: The bin, stating how often the transition happened.
340 """
341 return self._bin