Coverage for pyEDAA/Reports/CodeCoverage/UCIS/ToggleBits.py: 77%
122 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-21 23:49 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-21 23:49 +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 TypeError: If parameter ``userAttributes`` isn't iterable.
96 :raises TypeError: If parameter ``userAttributes`` contains an element not of type
97 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`.
98 :raises TypeError: If parameter ``alias`` isn't of type :class:`str`.
99 :raises ValueError: If parameter ``excluded`` is ``None``.
100 :raises TypeError: If parameter ``excluded`` isn't of type :class:`bool`.
101 :raises TypeError: If parameter ``excludedReason`` isn't of type :class:`str`.
102 :raises ValueError: If parameter ``weight`` is ``None``.
103 :raises TypeError: If parameter ``weight`` isn't of type :class:`int`.
104 :raises ValueError: If parameter ``weight`` is negative.
105 :raises ValueError: If parameter ``name`` or ``key`` is ``None``.
106 :raises TypeError: If parameter ``name`` or ``key`` isn't of type :class:`str`.
107 :raises TypeError: If parameter ``parent`` isn't of type
108 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Toggles.ToggleObject`.
109 :raises TypeError: If parameter ``indices`` isn't iterable.
110 :raises TypeError: If parameter ``indices`` contains an element not of type :class:`int`.
111 :raises ValueError: If parameter ``indices`` contains a negative index.
112 """
113 from pyEDAA.Reports.CodeCoverage.UCIS.Toggles import ToggleObject
115 super().__init__(userAttributes)
116 ObjectAttributesMixin.__init__(self, alias, excluded, excludedReason, weight)
118 for parameterName, value in (
119 ("name", name),
120 ("key", key)
121 ):
122 if value is None: 122 ↛ 123line 122 didn't jump to line 123 because the condition on line 122 was never true
123 raise ValueError(f"Parameter '{parameterName}' is None.")
124 elif not isinstance(value, str): 124 ↛ 125line 124 didn't jump to line 125 because the condition on line 124 was never true
125 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.")
126 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
127 raise ex
129 if parent is not None and not isinstance(parent, ToggleObject): 129 ↛ 130line 129 didn't jump to line 130 because the condition on line 129 was never true
130 ex = TypeError("Parameter 'parent' is not of type 'ToggleObject'.")
131 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.")
132 raise ex
134 self._parent = parent
135 self._name = name
136 self._key = key
137 self._indices = []
138 self._toggles = []
140 if indices is not None: 140 ↛ 158line 140 didn't jump to line 158 because the condition on line 140 was always true
141 if not isinstance(indices, Iterable): 141 ↛ 142line 141 didn't jump to line 142 because the condition on line 141 was never true
142 ex = TypeError("Parameter 'indices' is not iterable.")
143 ex.add_note(f"Got type '{getFullyQualifiedName(indices)}'.")
144 raise ex
146 for index in indices:
147 if not isinstance(index, int) or isinstance(index, bool): 147 ↛ 148line 147 didn't jump to line 148 because the condition on line 147 was never true
148 ex = TypeError("Parameter 'indices' contains an element not of type 'int'.")
149 ex.add_note(f"Got type '{getFullyQualifiedName(index)}'.")
150 raise ex
151 elif index < 0:
152 ex = ValueError("Parameter 'indices' contains a negative index.")
153 ex.add_note(f"Got value '{index}'.")
154 raise ex
156 self._indices.append(index)
158 if parent is not None: 158 ↛ exitline 158 didn't return from function '__init__' because the condition on line 158 was always true
159 parent._bits.append(self)
161 @classmethod
162 def Parse(cls, element: _Element, *, parent: Nullable[ToggleObject] = None) -> Self:
163 """
164 Parse a toggle bit and its transitions from its ``<toggleBit>`` element.
166 :param element: The ``<toggleBit>`` element.
167 :param parent: Optional, the toggle object the bit belongs to. Default: ``None``.
168 :returns: The toggle bit.
169 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states.
170 """
171 toggleBit = cls(
172 element.attrib["name"],
173 element.attrib["key"],
174 [int(indexElement.text) for indexElement in element.iterfind("{*}index")],
175 element.attrib.get("alias"),
176 cls._ParseBoolean(element.attrib.get("excluded", "false")),
177 element.attrib.get("excludedReason"),
178 int(element.attrib.get("weight", "1")),
179 cls._ParseUserAttributes(element),
180 parent=parent
181 )
183 for toggleElement in element.iterfind("{*}toggle"):
184 Toggle.Parse(toggleElement, parent=toggleBit)
186 return toggleBit
188 @readonly
189 def Parent(self) -> Nullable[ToggleObject]:
190 """
191 Read-only property to access the toggle object the bit belongs to (:attr:`_parent`).
193 :returns: The toggle object; ``None``, if the bit belongs to none.
194 """
195 return self._parent
197 @readonly
198 def Name(self) -> str:
199 """
200 Read-only property to access the name of the bit (:attr:`_name`).
202 :returns: The name, e.g. ``ff1[2]``.
203 """
204 return self._name
206 @readonly
207 def Key(self) -> str:
208 """
209 Read-only property to access the UCIS key of the bit (:attr:`_key`).
211 :returns: The key, as stated.
212 """
213 return self._key
215 @readonly
216 def Indices(self) -> list[int]:
217 """
218 Read-only property to access the indices of the bit (:attr:`_indices`).
220 :returns: The indices, one per dimension; empty, if the report states none.
221 """
222 return self._indices
224 @readonly
225 def Toggles(self) -> list[Toggle]:
226 """
227 Read-only property to access the transitions of the bit (:attr:`_toggles`).
229 :returns: The transitions, in the order the report lists them.
230 """
231 return self._toggles
234@export
235class Toggle(metaclass=ExtendedType, slots=True):
236 """
237 A ``<toggle>`` of a toggle bit: a transition from one value to another, and its bin - how often it happened.
238 """
240 _parent: Nullable[ToggleBit] #: The toggle bit the transition belongs to.
241 _from: str #: The value before the transition, e.g. ``0``.
242 _to: str #: The value after the transition, e.g. ``1``.
243 _bin: _Bin #: How often the transition happened.
245 def __init__(self, fromValue: str, toValue: str, coverBin: Bin, *, parent: Nullable[ToggleBit] = None) -> None:
246 """
247 Initialize the transition, become its bin's parent, and append it to the transitions of its toggle bit.
249 :param fromValue: The value before the transition, e.g. ``0``.
250 :param toValue: The value after the transition, e.g. ``1``.
251 :param coverBin: How often the transition happened.
252 :param parent: Optional, the toggle bit the transition belongs to. Default: ``None``.
253 :raises ValueError: If parameter ``fromValue`` or ``toValue`` is ``None``.
254 :raises TypeError: If parameter ``fromValue`` or ``toValue`` isn't of type :class:`str`.
255 :raises ValueError: If parameter ``coverBin`` is ``None``.
256 :raises TypeError: If parameter ``coverBin`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.Bin`.
257 :raises TypeError: If parameter ``parent`` isn't of type :class:`ToggleBit`.
258 """
259 for parameterName, value in (
260 ("fromValue", fromValue),
261 ("toValue", toValue)
262 ):
263 if value is None: 263 ↛ 264line 263 didn't jump to line 264 because the condition on line 263 was never true
264 raise ValueError(f"Parameter '{parameterName}' is None.")
265 elif not isinstance(value, str): 265 ↛ 266line 265 didn't jump to line 266 because the condition on line 265 was never true
266 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.")
267 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
268 raise ex
270 if coverBin is None: 270 ↛ 271line 270 didn't jump to line 271 because the condition on line 270 was never true
271 raise ValueError("Parameter 'coverBin' is None.")
272 elif not isinstance(coverBin, Bin): 272 ↛ 273line 272 didn't jump to line 273 because the condition on line 272 was never true
273 ex = TypeError("Parameter 'coverBin' is not of type 'Bin'.")
274 ex.add_note(f"Got type '{getFullyQualifiedName(coverBin)}'.")
275 raise ex
277 if parent is not None and not isinstance(parent, ToggleBit):
278 ex = TypeError("Parameter 'parent' is not of type 'ToggleBit'.")
279 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.")
280 raise ex
282 self._parent = parent
283 self._from = fromValue
284 self._to = toValue
285 self._bin = coverBin
286 coverBin._parent = self
288 if parent is not None: 288 ↛ exitline 288 didn't return from function '__init__' because the condition on line 288 was always true
289 parent._toggles.append(self)
291 @classmethod
292 def Parse(cls, element: _Element, *, parent: Nullable[ToggleBit] = None) -> Self:
293 """
294 Parse a transition and its bin from its ``<toggle>`` element.
296 :param element: The ``<toggle>`` element.
297 :param parent: Optional, the toggle bit the transition belongs to. Default: ``None``.
298 :returns: The transition.
299 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states.
300 """
301 return cls(element.attrib["from"], element.attrib["to"], Bin.Parse(element.find("{*}bin")), parent=parent)
303 @readonly
304 def Parent(self) -> Nullable[ToggleBit]:
305 """
306 Read-only property to access the toggle bit the transition belongs to (:attr:`_parent`).
308 :returns: The toggle bit; ``None``, if the transition belongs to none.
309 """
310 return self._parent
312 @readonly
313 def From(self) -> str:
314 """
315 Read-only property to access the value before the transition (:attr:`_from`).
317 :returns: The value, as stated, e.g. ``0``.
318 """
319 return self._from
321 @readonly
322 def To(self) -> str:
323 """
324 Read-only property to access the value after the transition (:attr:`_to`).
326 :returns: The value, as stated, e.g. ``1``.
327 """
328 return self._to
330 @readonly
331 def Bin(self) -> Bin:
332 """
333 Read-only property to access the transition's bin (:attr:`_bin`).
335 :returns: The bin, stating how often the transition happened.
336 """
337 return self._bin