Coverage for pyEDAA/Reports/CodeCoverage/UCIS/Toggles.py: 77%

189 statements  

« 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 toggle coverage of the UCIS XML interchange format: an instance's ``<toggleCoverage>``, its metric modes, and its 

33signals - toggle objects - with their dimensions. 

34""" 

35from __future__ import annotations 

36 

37from typing import TYPE_CHECKING, Iterable, Optional as Nullable, Self 

38 

39from lxml.etree import _Element 

40from pyTooling.Common import getFullyQualifiedName 

41from pyTooling.Decorators import export, readonly 

42from pyTooling.MetaClasses import ExtendedType 

43 

44from pyEDAA.Reports.CodeCoverage.UCIS.Elements import Base, MetricCoverage, ObjectAttributesMixin, StatementID 

45from pyEDAA.Reports.CodeCoverage.UCIS.Elements import UserAttribute 

46from pyEDAA.Reports.CodeCoverage.UCIS.ToggleBits import ToggleBit 

47 

48if TYPE_CHECKING: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true

49 from pyEDAA.Reports.CodeCoverage.UCIS.Instances import InstanceCoverage 

50 

51 

52@export 

53class MetricMode(Base): 

54 """ 

55 A ``<metricMode>`` of a toggle coverage: a mode, in which the coverage was measured, and its user-defined attributes. 

56 """ 

57 

58 _parent: Nullable[ToggleCoverage] #: The toggle coverage the metric mode belongs to. 

59 _metricMode: str #: Name of the mode, e.g. ``2STOGGLE``. 

60 

61 def __init__( 

62 self, 

63 metricMode: str, 

64 userAttributes: Nullable[Iterable[UserAttribute]] = None, 

65 *, 

66 parent: Nullable[ToggleCoverage] = None 

67 ) -> None: 

68 """ 

69 Initialize the metric mode, and append it to the metric modes of its toggle coverage. 

70 

71 :param metricMode: Name of the mode, e.g. ``2STOGGLE``. 

72 :param userAttributes: Optional, the user-defined attributes. Default: ``None``. 

73 :param parent: Optional, the toggle coverage the metric mode belongs to. Default: ``None``. 

74 :raises TypeError: If parameter ``userAttributes`` isn't iterable. 

75 :raises TypeError: If parameter ``userAttributes`` contains an element not of type 

76 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. 

77 :raises ValueError: If parameter ``metricMode`` is ``None``. 

78 :raises TypeError: If parameter ``metricMode`` isn't of type :class:`str`. 

79 :raises TypeError: If parameter ``parent`` isn't of type :class:`ToggleCoverage`. 

80 """ 

81 super().__init__(userAttributes) 

82 

83 if metricMode is None: 83 ↛ 84line 83 didn't jump to line 84 because the condition on line 83 was never true

84 raise ValueError("Parameter 'metricMode' is None.") 

85 elif not isinstance(metricMode, str): 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true

86 ex = TypeError("Parameter 'metricMode' is not of type 'str'.") 

87 ex.add_note(f"Got type '{getFullyQualifiedName(metricMode)}'.") 

88 raise ex 

89 

90 if parent is not None and not isinstance(parent, ToggleCoverage): 90 ↛ 91line 90 didn't jump to line 91 because the condition on line 90 was never true

91 ex = TypeError("Parameter 'parent' is not of type 'ToggleCoverage'.") 

92 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") 

93 raise ex 

94 

95 self._parent = parent 

96 self._metricMode = metricMode 

97 

98 if parent is not None: 98 ↛ exitline 98 didn't return from function '__init__' because the condition on line 98 was always true

99 parent._metricModes.append(self) 

100 

101 @classmethod 

102 def Parse(cls, element: _Element, *, parent: Nullable[ToggleCoverage] = None) -> Self: 

103 """ 

104 Parse a metric mode from its ``<metricMode>`` element. 

105 

106 :param element: The ``<metricMode>`` element. 

107 :param parent: Optional, the toggle coverage the metric mode belongs to. Default: ``None``. 

108 :returns: The metric mode. 

109 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. 

110 """ 

111 return cls(element.attrib["metricMode"], cls._ParseUserAttributes(element), parent=parent) 

112 

113 @readonly 

114 def Parent(self) -> Nullable[ToggleCoverage]: 

115 """ 

116 Read-only property to access the toggle coverage the metric mode belongs to (:attr:`_parent`). 

117 

118 :returns: The toggle coverage; ``None``, if the metric mode belongs to none. 

119 """ 

120 return self._parent 

121 

122 @readonly 

123 def MetricMode(self) -> str: 

124 """ 

125 Read-only property to access the name of the mode (:attr:`_metricMode`). 

126 

127 :returns: The name, e.g. ``2STOGGLE``. 

128 """ 

129 return self._metricMode 

130 

131 

132@export 

133class ToggleCoverage(MetricCoverage): 

134 """ 

135 A ``<toggleCoverage>`` of an instance: its toggle objects and its metric modes. 

136 """ 

137 

138 _objects: list[ToggleObject] #: The toggle objects. 

139 _metricModes: list[MetricMode] #: The metric modes. 

140 

141 def __init__( 

142 self, 

143 metricMode: Nullable[str] = None, 

144 weight: int = 1, 

145 userAttributes: Nullable[Iterable[UserAttribute]] = None, 

146 *, 

147 parent: Nullable[InstanceCoverage] = None 

148 ) -> None: 

149 """ 

150 Initialize the toggle coverage, and append it to the toggle coverages of its instance. 

151 

152 Its toggle objects and metric modes are added by creating them with this coverage as their parent. 

153 

154 :param metricMode: Optional, the mode in which the coverage was measured. Default: ``None``. 

155 :param weight: Optional, weight of the mode in computing the coverage. Default: ``1``. 

156 :param userAttributes: Optional, the user-defined attributes. Default: ``None``. 

157 :param parent: Optional, the instance the coverage belongs to. Default: ``None``. 

158 :raises TypeError: If parameter ``userAttributes`` isn't iterable. 

159 :raises TypeError: If parameter ``userAttributes`` contains an element not of type 

160 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. 

161 :raises TypeError: If parameter ``metricMode`` isn't of type :class:`str`. 

162 :raises ValueError: If parameter ``weight`` is ``None``. 

163 :raises TypeError: If parameter ``weight`` isn't of type :class:`int`. 

164 :raises ValueError: If parameter ``weight`` is negative. 

165 :raises TypeError: If parameter ``parent`` isn't of type 

166 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Instances.InstanceCoverage`. 

167 """ 

168 super().__init__(metricMode, weight, userAttributes, parent=parent) 

169 

170 self._objects = [] 

171 self._metricModes = [] 

172 

173 if parent is not None: 

174 parent._toggleCoverages.append(self) 

175 

176 @classmethod 

177 def Parse(cls, element: _Element, *, parent: Nullable[InstanceCoverage] = None) -> Self: 

178 """ 

179 Parse a toggle coverage, its toggle objects and metric modes from its ``<toggleCoverage>`` element. 

180 

181 :param element: The ``<toggleCoverage>`` element. 

182 :param parent: Optional, the instance the coverage belongs to. Default: ``None``. 

183 :returns: The toggle coverage. 

184 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. 

185 """ 

186 toggleCoverage = cls( 

187 element.attrib.get("metricMode"), 

188 int(element.attrib.get("weight", "1")), 

189 cls._ParseUserAttributes(element), 

190 parent=parent 

191 ) 

192 

193 for objectElement in element.iterfind("{*}toggleObject"): 

194 ToggleObject.Parse(objectElement, parent=toggleCoverage) 

195 

196 for modeElement in element.iterfind("{*}metricMode"): 

197 MetricMode.Parse(modeElement, parent=toggleCoverage) 

198 

199 return toggleCoverage 

200 

201 @readonly 

202 def Objects(self) -> list[ToggleObject]: 

203 """ 

204 Read-only property to access the toggle objects (:attr:`_objects`). 

205 

206 :returns: The toggle objects, in the order the report lists them. 

207 """ 

208 return self._objects 

209 

210 @readonly 

211 def MetricModes(self) -> list[MetricMode]: 

212 """ 

213 Read-only property to access the metric modes (:attr:`_metricModes`). 

214 

215 :returns: The metric modes, in the order the report lists them. 

216 """ 

217 return self._metricModes 

218 

219 

220@export 

221class Dimension(metaclass=ExtendedType, slots=True): 

222 """ 

223 A ``<dimension>`` of a toggle object: its left and right index, and whether the index descends. 

224 

225 It is a value, as a path is: it names no parent. 

226 """ 

227 

228 _left: int #: The left index. 

229 _right: int #: The right index. 

230 _downTo: bool #: Whether the index descends from left to right. 

231 

232 def __init__(self, left: int, right: int, downTo: bool) -> None: 

233 """ 

234 Initialize the dimension. 

235 

236 :param left: The left index. 

237 :param right: The right index. 

238 :param downTo: Whether the index descends from left to right, e.g. for ``[3:0]``. 

239 :raises ValueError: If parameter ``left`` or ``right`` is ``None``. 

240 :raises TypeError: If parameter ``left`` or ``right`` isn't of type :class:`int`. 

241 :raises ValueError: If parameter ``downTo`` is ``None``. 

242 :raises TypeError: If parameter ``downTo`` isn't of type :class:`bool`. 

243 """ 

244 if left is None: 244 ↛ 245line 244 didn't jump to line 245 because the condition on line 244 was never true

245 raise ValueError("Parameter 'left' is None.") 

246 elif not isinstance(left, int) or isinstance(left, bool): 246 ↛ 247line 246 didn't jump to line 247 because the condition on line 246 was never true

247 ex = TypeError("Parameter 'left' is not of type 'int'.") 

248 ex.add_note(f"Got type '{getFullyQualifiedName(left)}'.") 

249 raise ex 

250 

251 if right is None: 251 ↛ 252line 251 didn't jump to line 252 because the condition on line 251 was never true

252 raise ValueError("Parameter 'right' is None.") 

253 elif not isinstance(right, int) or isinstance(right, bool): 253 ↛ 254line 253 didn't jump to line 254 because the condition on line 253 was never true

254 ex = TypeError("Parameter 'right' is not of type 'int'.") 

255 ex.add_note(f"Got type '{getFullyQualifiedName(right)}'.") 

256 raise ex 

257 

258 if downTo is None: 258 ↛ 259line 258 didn't jump to line 259 because the condition on line 258 was never true

259 raise ValueError("Parameter 'downTo' is None.") 

260 elif not isinstance(downTo, bool): 

261 ex = TypeError("Parameter 'downTo' is not of type 'bool'.") 

262 ex.add_note(f"Got type '{getFullyQualifiedName(downTo)}'.") 

263 raise ex 

264 

265 self._left = left 

266 self._right = right 

267 self._downTo = downTo 

268 

269 @classmethod 

270 def Parse(cls, element: _Element) -> Self: 

271 """ 

272 Parse a dimension from its ``<dimension>`` element. 

273 

274 :param element: The ``<dimension>`` element. 

275 :returns: The dimension. 

276 """ 

277 return cls( 

278 int(element.attrib["left"]), 

279 int(element.attrib["right"]), 

280 Base._ParseBoolean(element.attrib["downto"]) 

281 ) 

282 

283 @readonly 

284 def Left(self) -> int: 

285 """ 

286 Read-only property to access the left index (:attr:`_left`). 

287 

288 :returns: The left index. 

289 """ 

290 return self._left 

291 

292 @readonly 

293 def Right(self) -> int: 

294 """ 

295 Read-only property to access the right index (:attr:`_right`). 

296 

297 :returns: The right index. 

298 """ 

299 return self._right 

300 

301 @readonly 

302 def DownTo(self) -> bool: 

303 """ 

304 Read-only property to access whether the index descends from left to right (:attr:`_downTo`). 

305 

306 :returns: ``True``, if the index descends. 

307 """ 

308 return self._downTo 

309 

310 

311@export 

312class ToggleObject(Base, ObjectAttributesMixin): 

313 """ 

314 A ``<toggleObject>`` of a toggle coverage: a signal, its type, dimensions and bits, and where it is declared. 

315 """ 

316 

317 _parent: Nullable[ToggleCoverage] #: The toggle coverage the toggle object belongs to. 

318 _name: str #: Name of the signal. 

319 _key: str #: UCIS key of the toggle object. 

320 _id: StatementID #: Where the signal is declared. 

321 _type: Nullable[str] #: Declared type of the signal. 

322 _portDirection: Nullable[str] #: Direction of the signal, if it is a port. 

323 _dimensions: list[Dimension] #: The dimensions of the signal. 

324 _bits: list[ToggleBit] #: The bits of the signal. 

325 

326 def __init__( 

327 self, 

328 name: str, 

329 key: str, 

330 statementID: StatementID, 

331 objectType: Nullable[str] = None, 

332 portDirection: Nullable[str] = None, 

333 dimensions: Nullable[Iterable[Dimension]] = None, 

334 alias: Nullable[str] = None, 

335 excluded: bool = False, 

336 excludedReason: Nullable[str] = None, 

337 weight: int = 1, 

338 userAttributes: Nullable[Iterable[UserAttribute]] = None, 

339 *, 

340 parent: Nullable[ToggleCoverage] = None 

341 ) -> None: 

342 """ 

343 Initialize the toggle object, and append it to the toggle objects of its toggle coverage. 

344 

345 Its bits are added by creating them with this toggle object as their parent. 

346 

347 :param name: Name of the signal, e.g. ``ff1``. 

348 :param key: UCIS key of the toggle object. 

349 :param statementID: Where the signal is declared. 

350 :param objectType: Optional, declared type of the signal, e.g. ``wire``. Default: ``None``. 

351 :param portDirection: Optional, direction of the signal, if it is a port, e.g. ``input``. Default: ``None``. 

352 :param dimensions: Optional, the dimensions of the signal. Default: ``None``. 

353 :param alias: Optional, an alias of the signal's name. Default: ``None``. 

354 :param excluded: Optional, whether the signal is excluded from the coverage. Default: ``False``. 

355 :param excludedReason: Optional, why the signal is excluded. Default: ``None``. 

356 :param weight: Optional, weight of the signal in computing the coverage. Default: ``1``. 

357 :param userAttributes: Optional, the user-defined attributes. Default: ``None``. 

358 :param parent: Optional, the toggle coverage the toggle object belongs to. Default: ``None``. 

359 :raises TypeError: If parameter ``userAttributes`` isn't iterable. 

360 :raises TypeError: If parameter ``userAttributes`` contains an element not of type 

361 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.UserAttribute`. 

362 :raises TypeError: If parameter ``alias`` isn't of type :class:`str`. 

363 :raises ValueError: If parameter ``excluded`` is ``None``. 

364 :raises TypeError: If parameter ``excluded`` isn't of type :class:`bool`. 

365 :raises TypeError: If parameter ``excludedReason`` isn't of type :class:`str`. 

366 :raises ValueError: If parameter ``weight`` is ``None``. 

367 :raises TypeError: If parameter ``weight`` isn't of type :class:`int`. 

368 :raises ValueError: If parameter ``weight`` is negative. 

369 :raises ValueError: If parameter ``name`` or ``key`` is ``None``. 

370 :raises TypeError: If parameter ``name`` or ``key`` isn't of type :class:`str`. 

371 :raises ValueError: If parameter ``statementID`` is ``None``. 

372 :raises TypeError: If parameter ``statementID`` isn't of type 

373 :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Elements.StatementID`. 

374 :raises TypeError: If parameter ``objectType`` or ``portDirection`` isn't of type :class:`str`. 

375 :raises TypeError: If parameter ``parent`` isn't of type :class:`ToggleCoverage`. 

376 :raises TypeError: If parameter ``dimensions`` isn't iterable. 

377 :raises TypeError: If parameter ``dimensions`` contains an element not of type :class:`Dimension`. 

378 """ 

379 super().__init__(userAttributes) 

380 ObjectAttributesMixin.__init__(self, alias, excluded, excludedReason, weight) 

381 

382 for parameterName, value in ( 

383 ("name", name), 

384 ("key", key) 

385 ): 

386 if value is None: 386 ↛ 387line 386 didn't jump to line 387 because the condition on line 386 was never true

387 raise ValueError(f"Parameter '{parameterName}' is None.") 

388 elif not isinstance(value, str): 388 ↛ 389line 388 didn't jump to line 389 because the condition on line 388 was never true

389 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") 

390 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

391 raise ex 

392 

393 if statementID is None: 393 ↛ 394line 393 didn't jump to line 394 because the condition on line 393 was never true

394 raise ValueError("Parameter 'statementID' is None.") 

395 elif not isinstance(statementID, StatementID): 395 ↛ 396line 395 didn't jump to line 396 because the condition on line 395 was never true

396 ex = TypeError("Parameter 'statementID' is not of type 'StatementID'.") 

397 ex.add_note(f"Got type '{getFullyQualifiedName(statementID)}'.") 

398 raise ex 

399 

400 for parameterName, value in ( 

401 ("objectType", objectType), 

402 ("portDirection", portDirection) 

403 ): 

404 if value is not None and not isinstance(value, str): 404 ↛ 405line 404 didn't jump to line 405 because the condition on line 404 was never true

405 ex = TypeError(f"Parameter '{parameterName}' is not of type 'str'.") 

406 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

407 raise ex 

408 

409 if parent is not None and not isinstance(parent, ToggleCoverage): 409 ↛ 410line 409 didn't jump to line 410 because the condition on line 409 was never true

410 ex = TypeError("Parameter 'parent' is not of type 'ToggleCoverage'.") 

411 ex.add_note(f"Got type '{getFullyQualifiedName(parent)}'.") 

412 raise ex 

413 

414 self._parent = parent 

415 self._name = name 

416 self._key = key 

417 self._id = statementID 

418 self._type = objectType 

419 self._portDirection = portDirection 

420 self._dimensions = [] 

421 self._bits = [] 

422 

423 if dimensions is not None: 423 ↛ 437line 423 didn't jump to line 437 because the condition on line 423 was always true

424 if not isinstance(dimensions, Iterable): 424 ↛ 425line 424 didn't jump to line 425 because the condition on line 424 was never true

425 ex = TypeError("Parameter 'dimensions' is not iterable.") 

426 ex.add_note(f"Got type '{getFullyQualifiedName(dimensions)}'.") 

427 raise ex 

428 

429 for dimension in dimensions: 

430 if not isinstance(dimension, Dimension): 430 ↛ 431line 430 didn't jump to line 431 because the condition on line 430 was never true

431 ex = TypeError("Parameter 'dimensions' contains an element not of type 'Dimension'.") 

432 ex.add_note(f"Got type '{getFullyQualifiedName(dimension)}'.") 

433 raise ex 

434 

435 self._dimensions.append(dimension) 

436 

437 if parent is not None: 437 ↛ exitline 437 didn't return from function '__init__' because the condition on line 437 was always true

438 parent._objects.append(self) 

439 

440 @classmethod 

441 def Parse(cls, element: _Element, *, parent: Nullable[ToggleCoverage] = None) -> Self: 

442 """ 

443 Parse a toggle object and its bits from its ``<toggleObject>`` element. 

444 

445 :param element: The ``<toggleObject>`` element. 

446 :param parent: Optional, the toggle coverage the toggle object belongs to. Default: ``None``. 

447 :returns: The toggle object. 

448 :raises CodeCoverageError: If a user-defined attribute's value isn't of the type the attribute states. 

449 """ 

450 toggleObject = cls( 

451 element.attrib["name"], 

452 element.attrib["key"], 

453 StatementID.Parse(element.find("{*}id")), 

454 element.attrib.get("type"), 

455 element.attrib.get("portDirection"), 

456 [Dimension.Parse(dimensionElement) for dimensionElement in element.iterfind("{*}dimension")], 

457 element.attrib.get("alias"), 

458 cls._ParseBoolean(element.attrib.get("excluded", "false")), 

459 element.attrib.get("excludedReason"), 

460 int(element.attrib.get("weight", "1")), 

461 cls._ParseUserAttributes(element), 

462 parent=parent 

463 ) 

464 

465 for bitElement in element.iterfind("{*}toggleBit"): 

466 ToggleBit.Parse(bitElement, parent=toggleObject) 

467 

468 return toggleObject 

469 

470 @readonly 

471 def Parent(self) -> Nullable[ToggleCoverage]: 

472 """ 

473 Read-only property to access the toggle coverage the toggle object belongs to (:attr:`_parent`). 

474 

475 :returns: The toggle coverage; ``None``, if the toggle object belongs to none. 

476 """ 

477 return self._parent 

478 

479 @readonly 

480 def Name(self) -> str: 

481 """ 

482 Read-only property to access the name of the signal (:attr:`_name`). 

483 

484 :returns: The name, as declared. 

485 """ 

486 return self._name 

487 

488 @readonly 

489 def Key(self) -> str: 

490 """ 

491 Read-only property to access the UCIS key of the toggle object (:attr:`_key`). 

492 

493 :returns: The key, as stated. 

494 """ 

495 return self._key 

496 

497 @readonly 

498 def ID(self) -> StatementID: 

499 """ 

500 Read-only property to access where the signal is declared (:attr:`_id`). 

501 

502 :returns: The source statement identifier. 

503 """ 

504 return self._id 

505 

506 @readonly 

507 def Type(self) -> Nullable[str]: 

508 """ 

509 Read-only property to access the declared type of the signal (:attr:`_type`). 

510 

511 :returns: The type, e.g. ``wire``; ``None``, if the report states none. 

512 """ 

513 return self._type 

514 

515 @readonly 

516 def PortDirection(self) -> Nullable[str]: 

517 """ 

518 Read-only property to access the direction of the signal, if it is a port (:attr:`_portDirection`). 

519 

520 :returns: The direction, e.g. ``input``; ``None``, if the report states none. 

521 """ 

522 return self._portDirection 

523 

524 @readonly 

525 def Dimensions(self) -> list[Dimension]: 

526 """ 

527 Read-only property to access the dimensions of the signal (:attr:`_dimensions`). 

528 

529 :returns: The dimensions, from the left one. 

530 """ 

531 return self._dimensions 

532 

533 @readonly 

534 def Bits(self) -> list[ToggleBit]: 

535 """ 

536 Read-only property to access the bits of the signal (:attr:`_bits`). 

537 

538 :returns: The bits, in the order the report lists them. 

539 """ 

540 return self._bits