Coverage for pyEDAA/Reports/CodeCoverage/UCIS/Blocks.py: 81%

213 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 statement and block coverage of the UCIS XML interchange format: an instance's ``<blockCoverage>``, its statements, 

33blocks and processes. 

34""" 

35from __future__ import annotations 

36 

37from typing import TYPE_CHECKING, Generator, Generic, Iterable 

38from typing import Optional as Nullable, Self, TypeVar 

39 

40from lxml.etree import _Element 

41from pyTooling.Common import getFullyQualifiedName 

42from pyTooling.Decorators import export, readonly 

43 

44from pyEDAA.Reports.CodeCoverage.UCIS.Bins import Bin 

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

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

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__all__ = ["BlockParentType"] 

53 

54# A class with a property named like a class - ``Bin`` - can't name that class in the annotation of a field: the class 

55# body's namespace, where annotations are evaluated, binds the name to the property. 

56_Bin = Bin 

57 

58BlockParentType = TypeVar("BlockParentType", bound="BlockCoverage | Process | Block") 

59"""A type variable for the parent of a :class:`Block`: a :class:`BlockCoverage`, a :class:`Process` or a 

60:class:`Block`.""" 

61 

62 

63@export 

64class BlockCoverage(MetricCoverage): 

65 """ 

66 A ``<blockCoverage>`` of an instance: its statements, its blocks or its processes with their blocks. 

67 

68 The format states one of the three. 

69 """ 

70 

71 _statements: list[Statement] #: The statements. 

72 _blocks: list[Block[BlockCoverage]] #: The blocks. 

73 _processes: list[Process] #: The processes. 

74 

75 def __init__( 

76 self, 

77 metricMode: Nullable[str] = None, 

78 weight: int = 1, 

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

80 *, 

81 parent: Nullable[InstanceCoverage] = None 

82 ) -> None: 

83 """ 

84 Initialize the block coverage, and append it to the block coverages of its instance. 

85 

86 Its statements, blocks and processes are added by creating them with this coverage as their parent. 

87 

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

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

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

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

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

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

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

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

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

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

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

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

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

101 """ 

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

103 

104 self._statements = [] 

105 self._blocks = [] 

106 self._processes = [] 

107 

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

109 parent._blockCoverages.append(self) 

110 

111 @classmethod 

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

113 """ 

114 Parse a block coverage, its statements, blocks and processes from its ``<blockCoverage>`` element. 

115 

116 :param element: The ``<blockCoverage>`` element. 

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

118 :returns: The block coverage. 

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

120 """ 

121 blockCoverage = cls( 

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

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

124 cls._ParseUserAttributes(element), 

125 parent=parent 

126 ) 

127 

128 for statementElement in element.iterfind("{*}statement"): 

129 Statement.Parse(statementElement, parent=blockCoverage) 

130 

131 for blockElement in element.iterfind("{*}block"): 

132 Block.Parse(blockElement, parent=blockCoverage) 

133 

134 for processElement in element.iterfind("{*}process"): 

135 Process.Parse(processElement, parent=blockCoverage) 

136 

137 return blockCoverage 

138 

139 def IterateBlocks(self) -> Generator[Block, None, None]: 

140 """ 

141 Iterate the blocks: those of the coverage and those of its processes, each followed by the blocks nested in it. 

142 

143 :returns: A generator of the blocks. 

144 """ 

145 for block in self._blocks: 

146 yield block 

147 yield from block.IterateBlocks() 

148 

149 for process in self._processes: 

150 for block in process._blocks: 

151 yield block 

152 yield from block.IterateBlocks() 

153 

154 @readonly 

155 def Statements(self) -> list[Statement]: 

156 """ 

157 Read-only property to access the statements (:attr:`_statements`). 

158 

159 :returns: The statements, in the order the report lists them. 

160 """ 

161 return self._statements 

162 

163 @readonly 

164 def Blocks(self) -> list[Block[BlockCoverage]]: 

165 """ 

166 Read-only property to access the blocks, which belong to no process (:attr:`_blocks`). 

167 

168 :returns: The blocks, in the order the report lists them. 

169 """ 

170 return self._blocks 

171 

172 @readonly 

173 def Processes(self) -> list[Process]: 

174 """ 

175 Read-only property to access the processes (:attr:`_processes`). 

176 

177 :returns: The processes, in the order the report lists them. 

178 """ 

179 return self._processes 

180 

181 

182@export 

183class Statement(Base, ObjectAttributesMixin): 

184 """ 

185 A ``<statement>`` of a block coverage: where the statement is, and its bin - how often it ran. 

186 """ 

187 

188 _parent: Nullable[BlockCoverage] #: The block coverage the statement belongs to. 

189 _id: StatementID #: Where the statement is. 

190 _bin: _Bin #: How often the statement ran. 

191 

192 def __init__( 

193 self, 

194 statementID: StatementID, 

195 coverBin: Bin, 

196 alias: Nullable[str] = None, 

197 excluded: bool = False, 

198 excludedReason: Nullable[str] = None, 

199 weight: int = 1, 

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

201 *, 

202 parent: Nullable[BlockCoverage] = None 

203 ) -> None: 

204 """ 

205 Initialize the statement, become its bin's parent, and append it to the statements of its block coverage. 

206 

207 :param statementID: Where the statement is. 

208 :param coverBin: How often the statement ran. 

209 :param alias: Optional, an alias of the statement's name. Default: ``None``. 

210 :param excluded: Optional, whether the statement is excluded from the coverage. Default: ``False``. 

211 :param excludedReason: Optional, why the statement is excluded. Default: ``None``. 

212 :param weight: Optional, weight of the statement in computing the coverage. Default: ``1``. 

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

214 :param parent: Optional, the block coverage the statement belongs to. Default: ``None``. 

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

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

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

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

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

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

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

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

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

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

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

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

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

228 :raises ValueError: If parameter ``coverBin`` is ``None``. 

229 :raises TypeError: If parameter ``coverBin`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.Bin`. 

230 :raises TypeError: If parameter ``parent`` isn't of type :class:`BlockCoverage`. 

231 """ 

232 super().__init__(userAttributes) 

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

234 

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

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

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

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

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

240 raise ex 

241 

242 if coverBin is None: 242 ↛ 243line 242 didn't jump to line 243 because the condition on line 242 was never true

243 raise ValueError("Parameter 'coverBin' is None.") 

244 elif not isinstance(coverBin, Bin): 244 ↛ 245line 244 didn't jump to line 245 because the condition on line 244 was never true

245 ex = TypeError("Parameter 'coverBin' is not of type 'Bin'.") 

246 ex.add_note(f"Got type '{getFullyQualifiedName(coverBin)}'.") 

247 raise ex 

248 

249 if parent is not None and not isinstance(parent, BlockCoverage): 

250 ex = TypeError("Parameter 'parent' is not of type 'BlockCoverage'.") 

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

252 raise ex 

253 

254 self._parent = parent 

255 self._id = statementID 

256 self._bin = coverBin 

257 coverBin._parent = self 

258 

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

260 parent._statements.append(self) 

261 

262 @classmethod 

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

264 """ 

265 Parse a statement and its bin from its ``<statement>`` element. 

266 

267 :param element: The ``<statement>`` element. 

268 :param parent: Optional, the block coverage the statement belongs to. Default: ``None``. 

269 :returns: The statement. 

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

271 """ 

272 return cls( 

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

274 Bin.Parse(element.find("{*}bin")), 

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

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

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

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

279 cls._ParseUserAttributes(element), 

280 parent=parent 

281 ) 

282 

283 @readonly 

284 def Parent(self) -> Nullable[BlockCoverage]: 

285 """ 

286 Read-only property to access the block coverage the statement belongs to (:attr:`_parent`). 

287 

288 :returns: The block coverage; ``None``, if the statement belongs to none. 

289 """ 

290 return self._parent 

291 

292 @readonly 

293 def ID(self) -> StatementID: 

294 """ 

295 Read-only property to access where the statement is (:attr:`_id`). 

296 

297 :returns: The source statement identifier. 

298 """ 

299 return self._id 

300 

301 @readonly 

302 def Bin(self) -> Bin: 

303 """ 

304 Read-only property to access the statement's bin (:attr:`_bin`). 

305 

306 :returns: The bin, stating how often the statement ran. 

307 """ 

308 return self._bin 

309 

310 

311@export 

312class Process(Base, ObjectAttributesMixin): 

313 """ 

314 A ``<process>`` of a block coverage: its kind and its blocks. 

315 """ 

316 

317 _parent: Nullable[BlockCoverage] #: The block coverage the process belongs to. 

318 _processType: str #: Kind of the process, e.g. ``always``. 

319 _blocks: list[Block[Process]] #: The blocks of the process. 

320 

321 def __init__( 

322 self, 

323 processType: str, 

324 alias: Nullable[str] = None, 

325 excluded: bool = False, 

326 excludedReason: Nullable[str] = None, 

327 weight: int = 1, 

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

329 *, 

330 parent: Nullable[BlockCoverage] = None 

331 ) -> None: 

332 """ 

333 Initialize the process, and append it to the processes of its block coverage. 

334 

335 Its blocks are added by creating them with this process as their parent. 

336 

337 :param processType: Kind of the process, e.g. ``always`` or ``initial``. 

338 :param alias: Optional, an alias of the process' name. Default: ``None``. 

339 :param excluded: Optional, whether the process is excluded from the coverage. Default: ``False``. 

340 :param excludedReason: Optional, why the process is excluded. Default: ``None``. 

341 :param weight: Optional, weight of the process in computing the coverage. Default: ``1``. 

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

343 :param parent: Optional, the block coverage the process belongs to. Default: ``None``. 

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

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

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

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

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

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

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

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

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

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

354 :raises ValueError: If parameter ``processType`` is ``None``. 

355 :raises TypeError: If parameter ``processType`` isn't of type :class:`str`. 

356 :raises TypeError: If parameter ``parent`` isn't of type :class:`BlockCoverage`. 

357 """ 

358 super().__init__(userAttributes) 

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

360 

361 if processType is None: 361 ↛ 362line 361 didn't jump to line 362 because the condition on line 361 was never true

362 raise ValueError("Parameter 'processType' is None.") 

363 elif not isinstance(processType, str): 363 ↛ 364line 363 didn't jump to line 364 because the condition on line 363 was never true

364 ex = TypeError("Parameter 'processType' is not of type 'str'.") 

365 ex.add_note(f"Got type '{getFullyQualifiedName(processType)}'.") 

366 raise ex 

367 

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

369 ex = TypeError("Parameter 'parent' is not of type 'BlockCoverage'.") 

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

371 raise ex 

372 

373 self._parent = parent 

374 self._processType = processType 

375 self._blocks = [] 

376 

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

378 parent._processes.append(self) 

379 

380 @classmethod 

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

382 """ 

383 Parse a process and its blocks from its ``<process>`` element. 

384 

385 :param element: The ``<process>`` element. 

386 :param parent: Optional, the block coverage the process belongs to. Default: ``None``. 

387 :returns: The process. 

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

389 """ 

390 process = cls( 

391 element.attrib["processType"], 

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

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

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

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

396 cls._ParseUserAttributes(element), 

397 parent=parent 

398 ) 

399 

400 for blockElement in element.iterfind("{*}block"): 

401 Block.Parse(blockElement, parent=process) 

402 

403 return process 

404 

405 @readonly 

406 def Parent(self) -> Nullable[BlockCoverage]: 

407 """ 

408 Read-only property to access the block coverage the process belongs to (:attr:`_parent`). 

409 

410 :returns: The block coverage; ``None``, if the process belongs to none. 

411 """ 

412 return self._parent 

413 

414 @readonly 

415 def ProcessType(self) -> str: 

416 """ 

417 Read-only property to access the kind of the process (:attr:`_processType`). 

418 

419 :returns: The kind, e.g. ``always``. 

420 """ 

421 return self._processType 

422 

423 @readonly 

424 def Blocks(self) -> list[Block[Process]]: 

425 """ 

426 Read-only property to access the blocks of the process (:attr:`_blocks`). 

427 

428 :returns: The blocks, in the order the report lists them. 

429 """ 

430 return self._blocks 

431 

432 

433@export 

434class Block(Base, ObjectAttributesMixin, Generic[BlockParentType]): 

435 """ 

436 A ``<block>`` - or a ``<hierarchicalBlock>`` nested in a block -: where it is, the statements it contains, its bin - 

437 how often it ran - and the blocks nested in it. 

438 """ 

439 

440 _parent: Nullable[BlockParentType] #: The block coverage, process or block the block belongs to. 

441 _id: StatementID #: Where the block is. 

442 _bin: _Bin #: How often the block ran. 

443 _statementIDs: list[StatementID] #: Where the statements of the block are. 

444 _parentProcess: Nullable[str] #: Kind of the process the block is in, e.g. ``always``. 

445 _blocks: list[Block[Block]] #: The blocks nested in this one. 

446 

447 def __init__( 

448 self, 

449 blockID: StatementID, 

450 coverBin: Bin, 

451 statementIDs: Nullable[Iterable[StatementID]] = None, 

452 parentProcess: Nullable[str] = None, 

453 alias: Nullable[str] = None, 

454 excluded: bool = False, 

455 excludedReason: Nullable[str] = None, 

456 weight: int = 1, 

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

458 *, 

459 parent: Nullable[BlockParentType] = None 

460 ) -> None: 

461 """ 

462 Initialize the block, become its bin's parent, and append it to the blocks of its parent. 

463 

464 The blocks nested in it are added by creating them with this block as their parent. 

465 

466 :param blockID: Where the block is. 

467 :param coverBin: How often the block ran. 

468 :param statementIDs: Optional, where the statements of the block are. Default: ``None``. 

469 :param parentProcess: Optional, kind of the process the block is in, e.g. ``always``. Default: ``None``. 

470 :param alias: Optional, an alias of the block's name. Default: ``None``. 

471 :param excluded: Optional, whether the block is excluded from the coverage. Default: ``False``. 

472 :param excludedReason: Optional, why the block is excluded. Default: ``None``. 

473 :param weight: Optional, weight of the block in computing the coverage. Default: ``1``. 

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

475 :param parent: Optional, the block coverage, process or block the block belongs to. Default: ``None``. 

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

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

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

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

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

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

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

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

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

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

486 :raises ValueError: If parameter ``blockID`` is ``None``. 

487 :raises TypeError: If parameter ``blockID`` isn't of type 

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

489 :raises ValueError: If parameter ``coverBin`` is ``None``. 

490 :raises TypeError: If parameter ``coverBin`` isn't of type :class:`~pyEDAA.Reports.CodeCoverage.UCIS.Bins.Bin`. 

491 :raises TypeError: If parameter ``parentProcess`` isn't of type :class:`str`. 

492 :raises TypeError: If parameter ``parent`` isn't of type :class:`BlockCoverage`, :class:`Process` or 

493 :class:`Block`. 

494 :raises TypeError: If parameter ``statementIDs`` isn't iterable. 

495 :raises TypeError: If parameter ``statementIDs`` contains an element not of type 

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

497 """ 

498 super().__init__(userAttributes) 

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

500 

501 if blockID is None: 501 ↛ 502line 501 didn't jump to line 502 because the condition on line 501 was never true

502 raise ValueError("Parameter 'blockID' is None.") 

503 elif not isinstance(blockID, StatementID): 503 ↛ 504line 503 didn't jump to line 504 because the condition on line 503 was never true

504 ex = TypeError("Parameter 'blockID' is not of type 'StatementID'.") 

505 ex.add_note(f"Got type '{getFullyQualifiedName(blockID)}'.") 

506 raise ex 

507 

508 if coverBin is None: 508 ↛ 509line 508 didn't jump to line 509 because the condition on line 508 was never true

509 raise ValueError("Parameter 'coverBin' is None.") 

510 elif not isinstance(coverBin, Bin): 510 ↛ 511line 510 didn't jump to line 511 because the condition on line 510 was never true

511 ex = TypeError("Parameter 'coverBin' is not of type 'Bin'.") 

512 ex.add_note(f"Got type '{getFullyQualifiedName(coverBin)}'.") 

513 raise ex 

514 

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

516 ex = TypeError("Parameter 'parentProcess' is not of type 'str'.") 

517 ex.add_note(f"Got type '{getFullyQualifiedName(parentProcess)}'.") 

518 raise ex 

519 

520 if parent is not None and not isinstance(parent, (BlockCoverage, Process, Block)): 

521 ex = TypeError("Parameter 'parent' is not of type 'BlockCoverage', 'Process' or 'Block'.") 

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

523 raise ex 

524 

525 self._parent = parent 

526 self._id = blockID 

527 self._bin = coverBin 

528 self._statementIDs = [] 

529 self._parentProcess = parentProcess 

530 self._blocks = [] 

531 coverBin._parent = self 

532 

533 if statementIDs is not None: 

534 if not isinstance(statementIDs, Iterable): 534 ↛ 535line 534 didn't jump to line 535 because the condition on line 534 was never true

535 ex = TypeError("Parameter 'statementIDs' is not iterable.") 

536 ex.add_note(f"Got type '{getFullyQualifiedName(statementIDs)}'.") 

537 raise ex 

538 

539 for statementID in statementIDs: 

540 if not isinstance(statementID, StatementID): 540 ↛ 541line 540 didn't jump to line 541 because the condition on line 540 was never true

541 ex = TypeError("Parameter 'statementIDs' contains an element not of type 'StatementID'.") 

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

543 raise ex 

544 

545 self._statementIDs.append(statementID) 

546 

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

548 parent._blocks.append(self) 

549 

550 @classmethod 

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

552 """ 

553 Parse a block, its bin and the blocks nested in it from its ``<block>`` or ``<hierarchicalBlock>`` element. 

554 

555 :param element: The ``<block>`` or ``<hierarchicalBlock>`` element. 

556 :param parent: Optional, the block coverage, process or block the block belongs to. Default: ``None``. 

557 :returns: The block. 

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

559 """ 

560 block = cls( 

561 StatementID.Parse(element.find("{*}blockId")), 

562 Bin.Parse(element.find("{*}blockBin")), 

563 [StatementID.Parse(idElement) for idElement in element.iterfind("{*}statementId")], 

564 element.attrib.get("parentProcess"), 

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

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

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

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

569 cls._ParseUserAttributes(element), 

570 parent=parent 

571 ) 

572 

573 for blockElement in element.iterfind("{*}hierarchicalBlock"): 

574 Block.Parse(blockElement, parent=block) 

575 

576 return block 

577 

578 def IterateBlocks(self) -> Generator[Block[Block], None, None]: 

579 """ 

580 Iterate the blocks nested in this one, each followed by the blocks nested in it. 

581 

582 :returns: A generator of the blocks. 

583 """ 

584 for block in self._blocks: 

585 yield block 

586 yield from block.IterateBlocks() 

587 

588 @readonly 

589 def Parent(self) -> Nullable[BlockParentType]: 

590 """ 

591 Read-only property to access the block coverage, process or block the block belongs to (:attr:`_parent`). 

592 

593 :returns: The :class:`BlockCoverage`, :class:`Process` or :class:`Block`; ``None``, if the block belongs to none. 

594 """ 

595 return self._parent 

596 

597 @readonly 

598 def ID(self) -> StatementID: 

599 """ 

600 Read-only property to access where the block is (:attr:`_id`). 

601 

602 :returns: The source statement identifier. 

603 """ 

604 return self._id 

605 

606 @readonly 

607 def Bin(self) -> Bin: 

608 """ 

609 Read-only property to access the block's bin (:attr:`_bin`). 

610 

611 :returns: The bin, stating how often the block ran. 

612 """ 

613 return self._bin 

614 

615 @readonly 

616 def StatementIDs(self) -> list[StatementID]: 

617 """ 

618 Read-only property to access where the statements of the block are (:attr:`_statementIDs`). 

619 

620 :returns: The source statement identifiers, in the order the report lists them. 

621 """ 

622 return self._statementIDs 

623 

624 @readonly 

625 def ParentProcess(self) -> Nullable[str]: 

626 """ 

627 Read-only property to access the kind of the process the block is in (:attr:`_parentProcess`). 

628 

629 :returns: The kind, e.g. ``always``; ``None``, if the report states none. 

630 """ 

631 return self._parentProcess 

632 

633 @readonly 

634 def Blocks(self) -> list[Block[Block]]: 

635 """ 

636 Read-only property to access the blocks nested in this one (:attr:`_blocks`). 

637 

638 :returns: The blocks, in the order the report lists them. 

639 """ 

640 return self._blocks