Coverage for pyEDAA / OSVVM / Project / __init__.py: 85%

570 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-13 22:31 +0000

1# ==================================================================================================================== # 

2# _____ ____ _ _ ___ ______ ____ ____ __ # 

3# _ __ _ _| ____| _ \ / \ / \ / _ \/ ___\ \ / /\ \ / / \/ | # 

4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | \___ \\ \ / / \ \ / /| |\/| | # 

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| |___) |\ V / \ V / | | | | # 

6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/|____/ \_/ \_/ |_| |_| # 

7# |_| |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2025-2026 Patrick Lehmann - Boetzingen, Germany # 

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""" 

32Data model for OSVVM's ``*.pro`` files. 

33""" 

34from pathlib import Path 

35from typing import Optional as Nullable, List, Dict, Mapping, Iterable, TypeVar, Generic, Generator, NoReturn 

36 

37from pyTooling.Decorators import readonly, export 

38from pyTooling.MetaClasses import ExtendedType 

39from pyTooling.Common import getFullyQualifiedName 

40from pyVHDLModel import VHDLVersion 

41 

42from pyEDAA.OSVVM import OSVVMException 

43 

44 

45__all__ = ["osvvmContext", "_ParentType"] 

46 

47 

48_ParentType = TypeVar("_ParentType", bound="Base") 

49"""Type variable for the parent reference.""" 

50 

51 

52@export 

53class Base(Generic[_ParentType], metaclass=ExtendedType, slots=True): 

54 """ 

55 Base-class for all entities in the data model reflecting an OSVVM ``*.pro`` file. 

56 """ 

57 _parent: Nullable[_ParentType] #: Reference to a parent object. 

58 

59 def __init__(self, parent: Nullable[_ParentType] = None) -> None: 

60 """ 

61 Initializes the base-class with a parent reference. 

62 

63 :param parent: Optional, reference to a parent object. 

64 """ 

65 self._parent = parent 

66 

67 @readonly 

68 def Parent(self) -> Nullable[_ParentType]: 

69 """ 

70 Read-only property to access the parent object reference (:attr:`_parent`). 

71 

72 :returns: Parent object. 

73 """ 

74 return self._parent 

75 

76 

77@export 

78class Named(Base[_ParentType], Generic[_ParentType]): 

79 """ 

80 Base-class for all named classes in the data model reflecting an OSVVM ``*.pro`` file. 

81 """ 

82 _name: str #: Name of the entity. 

83 

84 def __init__( 

85 self, 

86 name: str, 

87 parent: Nullable[_ParentType] = None 

88 ) -> None: 

89 """ 

90 Initializes the base-class with a parent reference. 

91 

92 :param name: Name of the entity. 

93 :param parent: Optional, reference to a parent object. 

94 :raises TypeError: When parameter 'name' is not of type string. 

95 :raises ValueError: When parameter 'name' is empty. 

96 """ 

97 super().__init__(parent) 

98 

99 if not isinstance(name, str): # pragma: no cover 

100 ex = TypeError(f"Parameter 'name' is not a string.") 

101 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.") 

102 raise ex 

103 elif name == "": 103 ↛ 104line 103 didn't jump to line 104 because the condition on line 103 was never true

104 raise ValueError(f"Parameter 'name' is empty.") 

105 

106 self._name = name 

107 

108 @readonly 

109 def Name(self) -> str: 

110 """ 

111 Read-only property to access the entity's name (:attr:`_name`). 

112 

113 :returns: Name of the entity. 

114 """ 

115 return self._name 

116 

117 def __repr__(self) -> str: 

118 return f"{self.__class__.__name__}: {self._name}" 

119 

120 

121@export 

122class Option(metaclass=ExtendedType, slots=True): 

123 """ 

124 Base-class for all options in the data model used within an OSVVM ``*.pro`` file. 

125 """ 

126 

127 

128@export 

129class NoNullRangeWarning(Option): 

130 """ 

131 Analysis option: Disable null-range warnings for VHDL files at analysis. 

132 """ 

133 def __init__(self) -> None: 

134 """ 

135 Initializes this option. 

136 """ 

137 super().__init__() 

138 

139 def __repr__(self) -> str: 

140 return "NoNullRangeWarning" 

141 

142 

143@export 

144class SourceFile(Base[_ParentType], Generic[_ParentType]): 

145 """ 

146 A base-class describing any source file (VHDL, Verilog, ...) supported by OSVVM Scripts. 

147 """ 

148 

149 _path: Path #: Path to the source file. 

150 

151 def __init__( 

152 self, 

153 path: Path, 

154 parent: Nullable[Base[_ParentType]] = None 

155 ) -> None: 

156 """ 

157 Initializes a source file. 

158 

159 :param path: Path to the source file. 

160 :param parent: Reference to the parent entity. 

161 :raises TypeError: When parameter 'path' is not of type :class:`pathlib.Path`. 

162 """ 

163 super().__init__(parent) 

164 

165 if not isinstance(path, Path): # pragma: no cover 

166 ex = TypeError(f"Parameter 'path' is not a Path.") 

167 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.") 

168 raise ex 

169 

170 self._path = path 

171 

172 @readonly 

173 def Path(self) -> Path: 

174 """ 

175 Read-only property to access the path to the sourcefile (:attr:`_path`). 

176 

177 :returns: The sourcefile's path. 

178 """ 

179 return self._path 

180 

181 def __repr__(self) -> str: 

182 return f"{self.__class__.__name__}: {self._path}" 

183 

184 

185@export 

186class XDCConstraintFile(SourceFile): 

187 """ 

188 Represents an XDC constraint file. 

189 """ 

190 _scopeToRef: Nullable[str] #: Bind this constraint file to a reference within the design (e.g. VHDL entity name). 

191 _scopeToCell: Nullable[str] #: Bind this constraint file to a cell name within the design. 

192 

193 def __init__(self, path: Path, scopeToRef: Nullable[str], scopeToCell: Nullable[str]) -> None: 

194 """ 

195 Initializes an XDC constraint file. 

196 

197 :param path: Path to the XDC file. 

198 :param scopeToRef: Optional, ``ScopeToRef`` parameter for Vivado. 

199 :param scopeToCell: Optional, ``ScopeToCell`` parameter for Vivado. 

200 :raises TypeError: When parameter 'scopeToRef' is not of type string. 

201 :raises TypeError: When parameter 'scopeToCell' is not of type string. 

202 """ 

203 super().__init__(path) 

204 

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

206 ex = TypeError(f"Parameter 'scopeToRef' is not a str.") 

207 ex.add_note(f"Got type '{getFullyQualifiedName(scopeToRef)}'.") 

208 raise ex 

209 

210 self._scopeToRef = scopeToRef 

211 

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

213 ex = TypeError(f"Parameter 'scopeToCell' is not a str.") 

214 ex.add_note(f"Got type '{getFullyQualifiedName(scopeToCell)}'.") 

215 raise ex 

216 

217 self._scopeToCell = scopeToCell 

218 

219 def __repr__(self) -> str: 

220 properties = [] 

221 if self._scopeToRef is not None: 

222 properties.append(f"ScopeToRef={self._scopeToRef}") 

223 if self._scopeToCell is not None: 

224 properties.append(f"ScopeToCell={self._scopeToCell}") 

225 

226 props = f" {{{', '.join(properties)}}}" if len(properties) > 0 else "" 

227 

228 return f"{super().__repr__()}{props}" 

229 

230 

231@export 

232class VHDLSourceFile(SourceFile["VHDLLibrary"]): 

233 """ 

234 Represents a VHDL source file. 

235 """ 

236 _vhdlVersion: VHDLVersion #: VHDL language revision used for analyzing the file. 

237 _noNullRangeWarning: Nullable[bool] #: Optional setting, if null-range warnings should be suppressed while analysis. 

238 _associatedFiles: List[SourceFile] #: List of associated XDC files. 

239 

240 def __init__( 

241 self, 

242 path: Path, 

243 vhdlVersion: VHDLVersion = VHDLVersion.VHDL2008, 

244 vhdlLibrary: Nullable["VHDLLibrary"] = None, 

245 noNullRangeWarning: Nullable[bool] = None, 

246 associatedFiles: Nullable[Iterable[SourceFile]] = None 

247 ) -> None: 

248 """ 

249 Initializes a VHDL source file. 

250 

251 :param path: Path to the VHDL source file. 

252 :param vhdlVersion: VHDL language revision used for analysis. 

253 :param vhdlLibrary: Optional, VHDL library all contained design units are compiled into. 

254 :param noNullRangeWarning: Optional, suppress null-range warnings while analyzing. 

255 :param associatedFiles: Optional, list of associated files. 

256 :raises TypeError: When parameter 'vhdlLibrary' is not of type :class:`VHDLLibrary`. 

257 :raises TypeError: When parameter 'vhdlversion' is not of type :class:`~pyVHDLModel.VHDLVersion`. 

258 :raises TypeError: When parameter 'noNullRangeWarning' is not of type boolean. 

259 """ 

260 if vhdlLibrary is None: 

261 super().__init__(path) 

262 elif isinstance(vhdlLibrary, VHDLLibrary): 

263 super().__init__(path, vhdlLibrary) 

264 vhdlLibrary._files.append(self) 

265 else: # pragma: no cover 

266 ex = TypeError(f"Parameter 'vhdlLibrary' is not a Library.") 

267 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.") 

268 raise ex 

269 

270 if not isinstance(vhdlVersion, VHDLVersion): # pragma: no cover 

271 ex = TypeError(f"Parameter 'vhdlVersion' is not a VHDLVersion.") 

272 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlVersion)}'.") 

273 raise ex 

274 

275 self._vhdlVersion = vhdlVersion 

276 

277 if noNullRangeWarning is not None and not isinstance(noNullRangeWarning, bool): 277 ↛ 278line 277 didn't jump to line 278 because the condition on line 277 was never true

278 ex = TypeError(f"Parameter 'noNullRangeWarning' is not a boolean.") 

279 ex.add_note(f"Got type '{getFullyQualifiedName(noNullRangeWarning)}'.") 

280 raise ex 

281 

282 self._noNullRangeWarning = noNullRangeWarning 

283 # TODO: iterate and check element types 

284 self._associatedFiles = [] if associatedFiles is None else [f for f in associatedFiles] 

285 

286 @readonly 

287 def VHDLLibrary(self) -> Nullable["VHDLLibrary"]: 

288 """ 

289 Read-only property to access the VHDL file's VHDL library (:attr:`_parent`). 

290 

291 :returns: The VHDL library this file and its design units is compiled into. 

292 """ 

293 return self._parent 

294 

295 @property 

296 def VHDLVersion(self) -> VHDLVersion: 

297 """ 

298 Property to access the VHDL language revision (:attr:`_vhdlVersion`). 

299 

300 :returns: The used VHDL revision to analyze the file. 

301 """ 

302 return self._vhdlVersion 

303 

304 @VHDLVersion.setter 

305 def VHDLVersion(self, value: VHDLVersion) -> None: 

306 if not isinstance(value, VHDLVersion): 306 ↛ 307line 306 didn't jump to line 307 because the condition on line 306 was never true

307 ex = TypeError(f"Parameter 'value' is not a VHDLVersion.") 

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

309 raise ex 

310 

311 self._vhdlVersion = value 

312 

313 @property 

314 def NoNullRangeWarning(self) -> Nullable[bool]: 

315 """ 

316 Property to access the no-null-range-warning option (:attr:`_noNullRangeWarning`). 

317 

318 :returns: The option's value. 

319 """ 

320 return self._noNullRangeWarning 

321 

322 @NoNullRangeWarning.setter 

323 def NoNullRangeWarning(self, value: bool) -> None: 

324 if value is not None and not isinstance(value, bool): 

325 ex = TypeError(f"Parameter 'value' is not a boolean.") 

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

327 raise ex 

328 

329 self._noNullRangeWarning = value 

330 

331 @readonly 

332 def AssociatedFiles(self) -> List[SourceFile]: 

333 """ 

334 Read-only property to access the list of associated files (:attr:`_associatedFiles`). 

335 

336 :returns: The list of associated files. 

337 """ 

338 return self._associatedFiles 

339 

340 def __repr__(self) -> str: 

341 options = "" 

342 if self._noNullRangeWarning is not None: 342 ↛ 343line 342 didn't jump to line 343 because the condition on line 342 was never true

343 options += f", NoNullRangeWarning" 

344 return f"VHDLSourceFile: {self._path} ({self._vhdlVersion}{options})" 

345 

346 

347@export 

348class VHDLLibrary(Named["Build"]): 

349 """ 

350 A VHDL library collecting multiple VHDL files containing VHDL design units. 

351 """ 

352 

353 _files: List[VHDLSourceFile] #: VHDL source files within this VHDL library. 

354 

355 def __init__( 

356 self, 

357 name: str, 

358 vhdlFiles: Nullable[Iterable[VHDLSourceFile]] = None, 

359 build: Nullable["Build"] = None 

360 ) -> None: 

361 """ 

362 Initializes a VHDL library. 

363 

364 :param name: Name of the VHDL library. 

365 :param vhdlFiles: Optional, list of VHDL source files. 

366 :param build: Optional, parent reference to a :class:`Build`. 

367 :raises TypeError: When parameter 'name' is not of type string. 

368 :raises ValueError: When parameter 'name' is empty. 

369 :raises TypeError: When parameter 'build' is not of type :class:`Build`. 

370 :raises TypeError: When parameter 'vhdlFiles' is not an iterable. 

371 :raises TypeError: When parameter 'vhdlFiles' contains elements not of type :class:`VHDLSourceFile`. 

372 """ 

373 if build is None: 

374 super().__init__(name, None) 

375 elif isinstance(build, Build): 

376 super().__init__(name, build) 

377 build._vhdlLibraries[name] = self 

378 else: # pragma: no cover 

379 ex = TypeError(f"Parameter 'build' is not a Build.") 

380 ex.add_note(f"Got type '{getFullyQualifiedName(build)}'.") 

381 raise ex 

382 

383 self._files = [] 

384 if vhdlFiles is None: 

385 pass 

386 elif isinstance(vhdlFiles, Iterable): 

387 for vhdlFile in vhdlFiles: 

388 if not isinstance(vhdlFile, VHDLSourceFile): 388 ↛ 389line 388 didn't jump to line 389 because the condition on line 388 was never true

389 ex = TypeError(f"Parameter 'vhdlFiles' contains elements not of type VHDLSourceFile.") 

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

391 raise ex 

392 

393 vhdlFile._parent = self 

394 self._files.append(vhdlFile) 

395 else: # pragma: no cover 

396 ex = TypeError(f"Parameter 'vhdlFiles' is not an iterable of VHDLSourceFile.") 

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

398 raise ex 

399 

400 @readonly 

401 def Build(self) -> Nullable["Build"]: 

402 """ 

403 Read-only property to access the build object (:attr:`_parent`). 

404 

405 :returns: The parent object. 

406 """ 

407 return self._parent 

408 

409 @readonly 

410 def Files(self) -> List[SourceFile]: 

411 """ 

412 Read-only property to access the list of VHDl source files in this VHDL library (:attr:`_files`). 

413 

414 :returns: The list of VHDL source files in this VHDL library. 

415 """ 

416 return self._files 

417 

418 def AddFile(self, file: VHDLSourceFile) -> None: 

419 """ 

420 Add a file to this VHDL library. 

421 

422 :param file: VHDL source file to add. 

423 :raises TypeError: When parameter 'file' is not of type :class:`VHDLSourceFile`. 

424 """ 

425 if not isinstance(file, VHDLSourceFile): # pragma: no cover 

426 ex = TypeError(f"Parameter 'file' is not a VHDLSourceFile.") 

427 ex.add_note(f"Got type '{getFullyQualifiedName(file)}'.") 

428 raise ex 

429 

430 file._parent = self 

431 self._files.append(file) 

432 

433 def __repr__(self) -> str: 

434 return f"VHDLLibrary: {self._name}" 

435 

436 

437@export 

438class GenericValue(Option): 

439 """ 

440 Elaboration option: A generic value for a VHDL top-level entity. 

441 """ 

442 _name: str #: Name of the generic. 

443 _value: str #: Value of the generic. 

444 

445 def __init__( 

446 self, 

447 name: str, 

448 value: str 

449 ) -> None: 

450 """ 

451 Initializes a generic value. 

452 

453 :param name: Name of the generic. 

454 :param value: Value of the generic. 

455 :raises TypeError: When parameter 'name' us not of type string. 

456 :raises TypeError: When parameter 'value' us not of type string. 

457 """ 

458 super().__init__() 

459 

460 if not isinstance(name, str): # pragma: no cover 

461 ex = TypeError(f"Parameter 'name' is not a string.") 

462 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.") 

463 raise ex 

464 elif name == "": 464 ↛ 465line 464 didn't jump to line 465 because the condition on line 464 was never true

465 raise ValueError(f"Parameter 'name' is empty.") 

466 

467 self._name = name 

468 

469 if not isinstance(value, str): # pragma: no cover 

470 ex = TypeError(f"Parameter 'value' is not a string.") 

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

472 raise ex 

473 

474 self._value = value 

475 

476 @readonly 

477 def Name(self) -> str: 

478 """ 

479 Read-only property to access the generic's name (:attr:`_name`). 

480 

481 :returns: The parent object. 

482 """ 

483 return self._name 

484 

485 @readonly 

486 def Value(self) -> str: 

487 """ 

488 Read-only property to access the generic's value (:attr:`_value`). 

489 

490 :returns: The parent object. 

491 """ 

492 return self._value 

493 

494 def __repr__(self) -> str: 

495 return f"{self._name} = {self._value}" 

496 

497 

498@export 

499class ConstraintFile(Option): 

500 """ 

501 Associated file option: Associated constraint file for VHDL sourcefiles. 

502 """ 

503 _path: Path #: Path to the constraint file. 

504 _scopeToRef: Nullable[str] #: Optional, ScopeToRef binding name. 

505 _scopeToCell: Nullable[str] #: Optional, ScopeToCell binding name. 

506 

507 def __init__( 

508 self, 

509 path: Path, 

510 scopeToRef: Nullable[str] = None, 

511 scopeToCell: Nullable[str] = None 

512 ) -> None: 

513 """ 

514 Initialize a constraint file option. 

515 

516 :param path: Path to the constraint file. 

517 :param scopeToRef: Optional, ScopeToRef binding name. 

518 :param scopeToCell: Optional, ScopeToCell binding name. 

519 :raises TypeError: When parameter 'path' is not of type :class:`~pathlib.Path`. 

520 :raises TypeError: When parameter 'scopeToRef' is not of type string. 

521 :raises TypeError: When parameter 'scopeToCell' is not of type string. 

522 """ 

523 super().__init__() 

524 

525 if not isinstance(path, Path): # pragma: no cover 

526 ex = TypeError(f"Parameter 'path' is not a Path.") 

527 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.") 

528 raise ex 

529 

530 self._path = path 

531 

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

533 ex = TypeError(f"Parameter 'scopeToRef' is not a str.") 

534 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.") 

535 raise ex 

536 

537 self._scopeToRef = scopeToRef 

538 

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

540 ex = TypeError(f"Parameter 'scopeToCell' is not a str.") 

541 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.") 

542 raise ex 

543 

544 self._scopeToCell = scopeToCell 

545 

546 @readonly 

547 def Path(self) -> Path: 

548 """ 

549 Read-only property to access the constraint file's path (:attr:`_path`). 

550 

551 :returns: The constraint file's path. 

552 """ 

553 return self._path 

554 

555 @readonly 

556 def ScopeToRef(self) -> Nullable[str]: 

557 """ 

558 Read-only property to access the constraint file's binding to a reference in the design (:attr:`_scopeToRef`). 

559 

560 :returns: The ``ScopeToRef`` binding. 

561 """ 

562 return self._scopeToRef 

563 

564 @readonly 

565 def ScopeToCell(self) -> Nullable[str]: 

566 """ 

567 Read-only property to access the constraint file's binding to a reference in the design (:attr:`_scopeToCell`). 

568 

569 :returns: The ``ScopeToCell`` binding. 

570 """ 

571 return self._scopeToCell 

572 

573 def __repr__(self) -> str: 

574 properties = [] 

575 if self._scopeToRef is not None: 

576 properties.append(f"ScopeToRef={self._scopeToRef}") 

577 if self._scopeToCell is not None: 

578 properties.append(f"ScopeToCell={self._scopeToCell}") 

579 

580 props = f" {{{', '.join(properties)}}}" if len(properties) > 0 else "" 

581 

582 return f"{self._path}{props}" 

583 

584 

585@export 

586class ScopeToRef(Option): 

587 """ 

588 Constrain file option: ScopeToRef binding. 

589 """ 

590 _reference: str #: Reference name. 

591 

592 def __init__( 

593 self, 

594 reference: str 

595 ) -> None: 

596 """ 

597 Initialize a ScopeToRef binding. 

598 

599 :param reference: Reference name. 

600 :raises TypeError: When parameter 'reference' is not of type string. 

601 """ 

602 super().__init__() 

603 

604 if not isinstance(reference, str): # pragma: no cover 

605 ex = TypeError(f"Parameter 'reference' is not a string.") 

606 ex.add_note(f"Got type '{getFullyQualifiedName(reference)}'.") 

607 raise ex 

608 

609 self._reference = reference 

610 

611 @readonly 

612 def Reference(self) -> str: 

613 """ 

614 Read-only property to access the reference name (:attr:`_reference`). 

615 

616 :returns: The reference name. 

617 """ 

618 return self._reference 

619 

620 def __repr__(self) -> str: 

621 return f"{self._reference}" 

622 

623 

624@export 

625class ScopeToCell(Option): 

626 """ 

627 Constrain file option: ScopeToCell binding. 

628 """ 

629 _cell: str #: Cell name 

630 

631 def __init__( 

632 self, 

633 cell: str 

634 ) -> None: 

635 """ 

636 Initialize a ScopeToCell binding. 

637 

638 :param cell: Cell name. 

639 :raises TypeError: When parameter 'cell' is not of type string. 

640 """ 

641 super().__init__() 

642 

643 if not isinstance(cell, str): # pragma: no cover 

644 ex = TypeError(f"Parameter 'cell' is not a string.") 

645 ex.add_note(f"Got type '{getFullyQualifiedName(cell)}'.") 

646 raise ex 

647 

648 self._cell = cell 

649 

650 @readonly 

651 def Cell(self) -> str: 

652 """ 

653 Read-only property to access the cell name (:attr:`_cell`). 

654 

655 :returns: The cell name. 

656 """ 

657 return self._cell 

658 

659 def __repr__(self) -> str: 

660 return f"{self._cell}" 

661 

662 

663@export 

664class Testcase(Named["Testsuite"]): 

665 """ 

666 An OSVVM testcase. 

667 """ 

668 _toplevelName: Nullable[str] #: Name of the VHDL simulation toplevel entity or configuration. 

669 _generics: Dict[str, str] #: A dictionary of toplevel generic values. 

670 

671 def __init__( 

672 self, 

673 name: str, 

674 toplevelName: Nullable[str] = None, 

675 generics: Nullable[Iterable[GenericValue] | Mapping[str, str]] = None, 

676 testsuite: Nullable["Testsuite"] = None 

677 ) -> None: 

678 """ 

679 Initialize an OSVVM testcase. 

680 

681 :param name: Name of the testcase. 

682 :param toplevelName: Optional, name of the toplevel entity or configuration. 

683 :param generics: Optional, list or dictionary of generic values to run the simulation. 

684 :param testsuite: Optional, reference to the parent testsuite. 

685 :raises TypeError: When parameter 'name' is not of type string. 

686 :raises ValueError: When parameter 'name' is empty. 

687 :raises TypeError: When parameter 'testsuite' is not of type :class:`Testsuite`. 

688 :raises TypeError: When parameter 'toplevelName' is not of type string. 

689 :raises TypeError: When parameter 'generics' is not a mapping or iterable. 

690 :raises TypeError: When parameter 'generics' contains elements not of type :class:`GenericValue`. 

691 """ 

692 if testsuite is None: 

693 super().__init__(name, None) 

694 elif isinstance(testsuite, Testsuite): 

695 super().__init__(name, testsuite) 

696 testsuite._testcases[name] = self 

697 else: # pragma: no cover 

698 ex = TypeError(f"Parameter 'testsuite' is not a Testsuite.") 

699 ex.add_note(f"Got type '{getFullyQualifiedName(testsuite)}'.") 

700 raise ex 

701 

702 if toplevelName is not None and not isinstance(toplevelName, str): # pragma: no cover 

703 ex = TypeError(f"Parameter 'toplevelName' is not a string.") 

704 ex.add_note(f"Got type '{getFullyQualifiedName(toplevelName)}'.") 

705 raise ex 

706 

707 self._toplevelName = toplevelName 

708 

709 self._generics = {} 

710 if generics is None: 

711 pass 

712 elif isinstance(generics, Mapping): 

713 for key, value in generics.items(): 

714 # TODO: check for str and str? 

715 self._generics[key] = value 

716 elif isinstance(generics, Iterable): 

717 for item in generics: 

718 if not isinstance(item, GenericValue): # pragma: no cover 

719 ex = TypeError(f"Parameter 'generics' contains elements which are not of type GenericValue.") 

720 ex.add_note(f"Got type '{getFullyQualifiedName(item)}'.") 

721 raise ex 

722 

723 self._generics[item._name] = item._value 

724 else: # pragma: no cover 

725 ex = TypeError(f"Parameter 'generics' is not an iterable of GenericValue nor a dictionary of strings.") 

726 ex.add_note(f"Got type '{getFullyQualifiedName(generics)}'.") 

727 raise ex 

728 

729 @readonly 

730 def Testsuite(self) -> "Testsuite": 

731 """ 

732 Read-only property to access the parent testsuite (:attr:`_parent`). 

733 

734 :returns: The parent testsuite. 

735 """ 

736 return self._parent 

737 

738 @readonly 

739 def ToplevelName(self) -> str: 

740 """ 

741 Read-only property to access the testcases toplevel name (:attr:`_toplevelName`). 

742 

743 :returns: The toplevel name. 

744 """ 

745 return self._toplevelName 

746 

747 @readonly 

748 def Generics(self) -> Dict[str, str]: 

749 """ 

750 Read-only property to access the testcase's toplevel generics (:attr:`_generics`). 

751 

752 :returns: The dictionary of generic values for this testcase. 

753 """ 

754 return self._generics 

755 

756 # TODO: why is this not a setter? 

757 def SetToplevel(self, toplevelName: str) -> None: 

758 """ 

759 Set the testcase's toplevel entity or configuration. 

760 

761 :param toplevelName: Name of the toplevel. 

762 :raises TypeError: When parameter 'toplevelName' is not of type string. 

763 """ 

764 if not isinstance(toplevelName, str): # pragma: no cover 

765 ex = TypeError(f"Parameter 'toplevelName' is not a string.") 

766 ex.add_note(f"Got type '{getFullyQualifiedName(toplevelName)}'.") 

767 raise ex 

768 elif toplevelName == "": 768 ↛ 769line 768 didn't jump to line 769 because the condition on line 768 was never true

769 raise ValueError(f"Parameter 'toplevelName' is empty.") 

770 

771 self._toplevelName = toplevelName 

772 

773 def AddGeneric(self, genericValue: GenericValue) -> None: 

774 """ 

775 Add a generic value to this testcase. 

776 

777 :param genericValue: Generic value to be added. 

778 :raises TypeError: When parameter 'genericValue' is not of type :class:`GenericValue`. 

779 """ 

780 if not isinstance(genericValue, GenericValue): # pragma: no cover 

781 ex = TypeError(f"Parameter 'genericValue' is not a GenericValue.") 

782 ex.add_note(f"Got type '{getFullyQualifiedName(genericValue)}'.") 

783 raise ex 

784 

785 self._generics[genericValue._name] = genericValue._value 

786 

787 def __repr__(self) -> str: 

788 generics = f" - [{', '.join([f'{n}={v}' for n,v in self._generics.items()])}]" if len(self._generics) > 0 else "" 

789 return f"Testcase: {self._name}{generics}" 

790 

791 

792@export 

793class Testsuite(Named["Build"]): 

794 """ 

795 An OSVVM testsuite containing multiple OSVVM testcases. 

796 """ 

797 _testcases: Dict[str, Testcase] #: Dictionary of testcases. 

798 

799 def __init__( 

800 self, 

801 name: str, 

802 testcases: Nullable[Iterable[Testcase] | Mapping[str, Testcase]] = None, 

803 build: Nullable["Build"] = None 

804 ) -> None: 

805 """ 

806 Initialize an OSVVM testsuite. 

807 

808 :param name: Name of the testsuite. 

809 :param testcases: Optional, list or dictionary of testcases. 

810 :param build: Optional, reference to the parent build. 

811 :raises TypeError: When parameter 'name' is not of type string. 

812 :raises ValueError: When parameter 'name' is empty. 

813 :raises TypeError: When parameter 'build' is not of type :class:`Build`. 

814 :raises TypeError: When parameter 'testcases' is not an iterable or mapping. 

815 :raises TypeError: When parameter 'testcases' contains an element not of type :class:`Testcase`. 

816 """ 

817 if build is None: 

818 super().__init__(name, None) 

819 elif isinstance(build, Build): 

820 super().__init__(name, build) 

821 build._testsuites[name] = self 

822 else: # pragma: no cover 

823 ex = TypeError(f"Parameter 'build' is not a Build.") 

824 ex.add_note(f"Got type '{getFullyQualifiedName(build)}'.") 

825 raise ex 

826 

827 self._testcases = {} 

828 if testcases is None: 

829 pass 

830 elif isinstance(testcases, Mapping): 

831 for key, value in testcases.items(): 

832 # TODO: check types of key/value 

833 value._parent = self 

834 self._testcases[key] = value 

835 elif isinstance(testcases, Iterable): 

836 for item in testcases: 

837 if not isinstance(item, Testcase): # pragma: no cover 

838 ex = TypeError(f"Parameter 'testcases' contains elements not of type Testcase.") 

839 ex.add_note(f"Got type '{getFullyQualifiedName(item)}'.") 

840 raise ex 

841 

842 item._parent = self 

843 self._testcases[item._name] = item 

844 else: # pragma: no cover 

845 ex = TypeError(f"Parameter 'testcases' is not an iterable of Testcase nor a mapping of Testcase.") 

846 ex.add_note(f"Got type '{getFullyQualifiedName(testcases)}'.") 

847 raise ex 

848 

849 @readonly 

850 def Build(self) -> Nullable["Build"]: 

851 """ 

852 Read-only property to access the parent build (:attr:`_parent`). 

853 

854 :returns: The parent build. 

855 """ 

856 return self._parent 

857 

858 @readonly 

859 def Testcases(self) -> Dict[str, Testcase]: 

860 """ 

861 Read-only property to access the dictionary of testcases (:attr:`_testcases`). 

862 

863 :returns: The dictionary of testcases. 

864 """ 

865 return self._testcases 

866 

867 def AddTestcase(self, testcase: Testcase) -> None: 

868 """ 

869 Add a testcase to the testsuite. 

870 

871 :param testcase: Testcase to add. 

872 :raises TypeError: When parameter 'testcase' is not of type :class:`Testcase`. 

873 """ 

874 if not isinstance(testcase, Testcase): # pragma: no cover 

875 ex = TypeError(f"Parameter 'testcase' is not a Testcase.") 

876 ex.add_note(f"Got type '{getFullyQualifiedName(testcase)}'.") 

877 raise ex 

878 

879 testcase._parent = self 

880 self._testcases[testcase._name] = testcase 

881 

882 def __repr__(self) -> str: 

883 return f"Testsuite: {self._name}" 

884 

885 

886@export 

887class BuildName(Option): 

888 """OSVVM option: Name of a build.""" 

889 _name: str #: Name of the build. 

890 

891 def __init__( 

892 self, 

893 name: str, 

894 ) -> None: 

895 """ 

896 Initialize the build name option. 

897 

898 :param name: Name of the build 

899 :raises TypeError: When parameter 'name' is not of type string. 

900 """ 

901 super().__init__() 

902 

903 if not isinstance(name, str): # pragma: no cover 

904 ex = TypeError(f"Parameter 'name' is not a string.") 

905 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.") 

906 raise ex 

907 

908 self._name = name 

909 

910 @readonly 

911 def Name(self) -> str: 

912 """ 

913 Read-only property to access the build name (:attr:`_name`). 

914 

915 :returns: Name of the build. 

916 """ 

917 return self._name 

918 

919 def __repr__(self) -> str: 

920 return f"BuildName: {self._name}" 

921 

922 

923@export 

924class Build(Named["Project"]): 

925 """ 

926 An OSVVM build containing one or multiple OSVVM testsuites. 

927 """ 

928 _includedFiles: List[Path] #: List of loaded (included) ``*.pro`` files. 

929 _vhdlLibraries: Dict[str, VHDLLibrary] #: Dictionary of VHDL libraries. 

930 _testsuites: Dict[str, Testsuite] #: Dictionary of testsuites. 

931 

932 def __init__( 

933 self, 

934 name: str, 

935 vhdlLibraries: Nullable[Iterable[VHDLLibrary] | Mapping[str, VHDLLibrary]] = None, 

936 testsuites: Nullable[Iterable[Testsuite] | Mapping[str, Testsuite]] = None, 

937 project: Nullable[Base] = None 

938 ) -> None: 

939 """ 

940 Initialize an OSVVM build. 

941 

942 :param name: Name of the build. 

943 :param vhdlLibraries: Optional, list or dictionary of VHDL libraries. 

944 :param testsuites: Optional, list or dictionary of OSVVM testsuites. 

945 :param project: Optional, reference to the parent project. 

946 :raises TypeError: When parameter 'name' is not of type string. 

947 :raises ValueError: When parameter 'name' is empty. 

948 :raises TypeError: When parameter 'project' is not of type :class:`Project`. 

949 :raises TypeError: When parameter 'vhdlLibraries' is not an iterable or mapping. 

950 :raises TypeError: When parameter 'vhdlLibraries' contains an element not of type :class:`VHDLLibrary`. 

951 :raises TypeError: When parameter 'testsuites' is not an iterable or mapping. 

952 :raises TypeError: When parameter 'testsuites' contains an element not of type :class:`Testsuites`. 

953 """ 

954 if project is None: 

955 super().__init__(name, None) 

956 elif isinstance(project, Project): 

957 super().__init__(name, project) 

958 project._builds[name] = self 

959 else: # pragma: no cover 

960 ex = TypeError(f"Parameter 'project' is not a Project.") 

961 ex.add_note(f"Got type '{getFullyQualifiedName(project)}'.") 

962 raise ex 

963 

964 self._includedFiles = [] 

965 self._vhdlLibraries = {} 

966 if vhdlLibraries is None: 

967 pass 

968 elif isinstance(vhdlLibraries, Mapping): 

969 for key, value in vhdlLibraries.items(): 

970 # TODO: check used datatypes 

971 value._parent = self 

972 self._vhdlLibraries[key] = value 

973 elif isinstance(vhdlLibraries, Iterable): 

974 for item in vhdlLibraries: 

975 if not isinstance(item, VHDLLibrary): # pragma: no cover 

976 ex = TypeError(f"Parameter 'vhdlLibraries' contains elements not of type VHDLLibrary.") 

977 ex.add_note(f"Got type '{getFullyQualifiedName(item)}'.") 

978 raise ex 

979 

980 item._parent = self 

981 self._vhdlLibraries[item._name] = item 

982 else: # pragma: no cover 

983 ex = TypeError(f"Parameter 'libraries' is not an iterable of VHDLLibrary nor a mapping of VHDLLibrary.") 

984 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibraries)}'.") 

985 raise ex 

986 

987 self._testsuites = {} 

988 if testsuites is None: 

989 pass 

990 elif isinstance(testsuites, Mapping): 

991 for key, value in testsuites.items(): 

992 # TODO: check used datatypes 

993 value._parent = self 

994 self._testsuites[key] = value 

995 elif isinstance(testsuites, Iterable): 

996 for item in testsuites: 

997 if not isinstance(item, Testsuite): # pragma: no cover 

998 ex = TypeError(f"Parameter 'testsuites' contains elements not of type Testsuite.") 

999 ex.add_note(f"Got type '{getFullyQualifiedName(item)}'.") 

1000 raise ex 

1001 

1002 item._parent = self 

1003 self._testsuites[item._name] = item 

1004 else: # pragma: no cover 

1005 ex = TypeError(f"Parameter 'testsuites' is not an iterable of Testsuite nor a mapping of Testsuite.") 

1006 ex.add_note(f"Got type '{getFullyQualifiedName(testsuites)}'.") 

1007 raise ex 

1008 

1009 @readonly 

1010 def Project(self) -> Nullable["Project"]: 

1011 """ 

1012 Read-only property to access the parent project (:attr:`_parent`). 

1013 

1014 :returns: The parent project. 

1015 """ 

1016 return self._parent 

1017 

1018 @readonly 

1019 def IncludedFiles(self) -> Generator[Path, None, None]: 

1020 """ 

1021 Read-only property to return a generator for all included (loaded) ``*.pro`` files (:attr:`_includedFiles`). 

1022 

1023 :returns: The sequence of loaded ``*.pro`` files. 

1024 """ 

1025 return (file for file in self._includedFiles) 

1026 

1027 @readonly 

1028 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]: 

1029 """ 

1030 Read-only property to access the dictionary of VHDL libraries (:attr:`_vhdlLibraries`). 

1031 

1032 :returns: The dictionary of VHDL libraries. 

1033 """ 

1034 return self._vhdlLibraries 

1035 

1036 @readonly 

1037 def Testsuites(self) -> Dict[str, Testsuite]: 

1038 """ 

1039 Read-only property to access the dictionary of testsuites (:attr:`_testsuites`). 

1040 

1041 :returns: The dictionary of testsuites. 

1042 """ 

1043 return self._testsuites 

1044 

1045 def AddVHDLLibrary(self, vhdlLibrary: VHDLLibrary) -> None: 

1046 """ 

1047 Add a VHDL library to the build. 

1048 

1049 :param vhdlLibrary: VHDL library to add. 

1050 :raises TypeError: When parameter 'vhdlLibrary' is not of type :class:`VHDLLibrary`. 

1051 """ 

1052 if not isinstance(vhdlLibrary, VHDLLibrary): # pragma: no cover 

1053 ex = TypeError(f"Parameter 'vhdlLibrary' is not a VHDLLibrary.") 

1054 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.") 

1055 raise ex 

1056 

1057 vhdlLibrary._parent = self 

1058 self._vhdlLibraries[vhdlLibrary._name] = vhdlLibrary 

1059 

1060 def AddTestsuite(self, testsuite: Testsuite) -> None: 

1061 """ 

1062 Add a testsuite to the build. 

1063 

1064 :param testsuite: Testsuite to add. 

1065 :raises TypeError: When parameter 'testsuite' is not of type :class:`Testsuite`. 

1066 """ 

1067 if not isinstance(testsuite, Testsuite): # pragma: no cover 

1068 ex = TypeError(f"Parameter 'testsuite' is not a Testsuite.") 

1069 ex.add_note(f"Got type '{getFullyQualifiedName(testsuite)}'.") 

1070 raise ex 

1071 

1072 testsuite._parent = self 

1073 self._testsuites[testsuite._name] = testsuite 

1074 

1075 def __repr__(self) -> str: 

1076 return f"Build: {self._name}" 

1077 

1078 

1079@export 

1080class Project(Named): 

1081 """ 

1082 An OSVVM project containing one or multiple OSVVM builds. 

1083 """ 

1084 _builds: Dict[str, Build] #: Dictionary of builds 

1085 

1086 def __init__( 

1087 self, 

1088 name: str, 

1089 builds: Nullable[Iterable[Build] | Mapping[str, Build]] = None 

1090 ) -> None: 

1091 """ 

1092 Initializes an OSVVM project. 

1093 

1094 :param name: Name of the build. 

1095 :param builds: Optional, list or dictionary of OSVVM builds. 

1096 :raises TypeError: When parameter 'name' is not of type string. 

1097 :raises ValueError: When parameter 'name' is empty. 

1098 :raises TypeError: When parameter 'builds' is not an iterable or mapping. 

1099 :raises TypeError: When parameter 'builds' contains an element not of type :class:`Build`. 

1100 """ 

1101 super().__init__(name, None) 

1102 

1103 self._builds = {} 

1104 if builds is None: 

1105 pass 

1106 elif isinstance(builds, Mapping): 

1107 for key, value in builds.items(): 

1108 # TODO: check used datatypes 

1109 value._parent = self 

1110 self._builds[key] = value 

1111 elif isinstance(builds, Iterable): 

1112 for item in builds: 

1113 if not isinstance(item, Build): # pragma: no cover 

1114 ex = TypeError(f"Parameter 'builds' contains elements not of type Build.") 

1115 ex.add_note(f"Got type '{getFullyQualifiedName(item)}'.") 

1116 raise ex 

1117 

1118 item._parent = self 

1119 self._builds[item._name] = item 

1120 else: # pragma: no cover 

1121 ex = TypeError(f"Parameter 'builds' is not an iterable of Build nor a mapping of Build.") 

1122 ex.add_note(f"Got type '{getFullyQualifiedName(builds)}'.") 

1123 raise ex 

1124 

1125 @readonly 

1126 def Builds(self) -> Dict[str, Build]: 

1127 """ 

1128 Read-only property to access the dictionary of builds (:attr:`_builds`). 

1129 

1130 :returns: The dictionary of builds. 

1131 """ 

1132 return self._builds 

1133 

1134 @readonly 

1135 def IncludedFiles(self) -> Generator[Path, None, None]: 

1136 """ 

1137 Read-only property to return a generator for all included (loaded) ``*.pro`` files. 

1138 

1139 .. note:: 

1140 

1141 This generator iterates all builds (:attr:`_builds`) and returns a combined generator for all included files. 

1142 

1143 :returns: The sequence of loaded ``*.pro`` files. 

1144 """ 

1145 for build in self._builds.values(): 

1146 yield from build.IncludedFiles 

1147 

1148 def AddBuild(self, build: Build) -> None: 

1149 """ 

1150 Add a build to the project. 

1151 

1152 :param build: Build to add. 

1153 :raises TypeError: When parameter 'build' is not of type :class:`Build`. 

1154 """ 

1155 if not isinstance(build, Build): # pragma: no cover 

1156 ex = TypeError(f"Parameter 'build' is not a Build.") 

1157 ex.add_note(f"Got type '{getFullyQualifiedName(build)}'.") 

1158 raise ex 

1159 

1160 build._parent = self 

1161 self._builds[build._name] = build 

1162 

1163 def __repr__(self) -> str: 

1164 return f"Project: {self._name}" 

1165 

1166 

1167@export 

1168class Context(Base): 

1169 """ 

1170 The OSVVM TCL execution context. 

1171 

1172 When an OSVVM ``*.pro`` file is executed, it relies on a context for storing the currently objects and settings. For 

1173 example the currently used testsuite or the currently set VHDL language revision. 

1174 

1175 .. hint:: 

1176 

1177 The context stores the last seen exception within Python scripting in :attr:`_lastException`, because TCL doesn't 

1178 forward a raised Python exception through TCL back into the Python context. It just raises a generic 

1179 :exc:`~tkinter.TclError`. The helper function :func:`~pyEDAA.OSVVM.Project.TCL.getException` help to restore the 

1180 original Python exception using this context object. 

1181 """ 

1182 # _tcl: TclEnvironment 

1183 

1184 _processor: "OsvvmProFileProcessor" #: The TCL processor. 

1185 _lastException: Nullable[Exception] #: Last Python exception seen. 

1186 

1187 _workingDirectory: Path #: The working directory, where the processing started. 

1188 _currentDirectory: Path #: The virtual working directory, e.g. updated by including other ``*.pro`` files. 

1189 _includedFiles: List[Path] #: A list of used ``*.pro`` files. 

1190 

1191 _vhdlversion: VHDLVersion #: The currently set VHDL language revision. 

1192 

1193 _vhdlLibrary: Nullable[VHDLLibrary] #: The currently active VHDL library. 

1194 _vhdlLibraries: Dict[str, VHDLLibrary] #: A dictionary of known VHDL libraries. 

1195 

1196 _testsuite: Nullable[Testsuite] #: The currently active OSVVM testsuite. 

1197 _testsuites: Dict[str, Testsuite] #: A dictionary of known testsuites. 

1198 _testcase: Nullable[Testcase] #: The currently active OSVVM testcase. 

1199 _options: Dict[int, Option] #: A dictionary of gathered options. 

1200 

1201 _build: Nullable[Build] #: The currently active OSVVM build. 

1202 _builds: Dict[str, Build] #: A dictionary of known OSVVM builds. 

1203 

1204 def __init__(self) -> None: 

1205 """ 

1206 Initializes a TCL execution context for OSVVM ``*.pro`` file processing. 

1207 """ 

1208 super().__init__() 

1209 

1210 self._processor = None 

1211 self._lastException = None 

1212 

1213 self._workingDirectory = Path.cwd() 

1214 self._currentDirectory = self._workingDirectory 

1215 self._includedFiles = [] 

1216 

1217 self._vhdlversion = VHDLVersion.VHDL2008 

1218 

1219 self._vhdlLibrary = None 

1220 self._vhdlLibraries = {} 

1221 

1222 self._testcase = None 

1223 self._testsuite = None 

1224 self._testsuites = {} 

1225 self._options = {} 

1226 

1227 self._build = None 

1228 self._builds = {} 

1229 

1230 def Clear(self) -> None: 

1231 """ 

1232 Clear the TCL execution context. 

1233 """ 

1234 self._processor = None 

1235 self._lastException = None 

1236 

1237 self._workingDirectory = Path.cwd() 

1238 self._currentDirectory = self._workingDirectory 

1239 self._includedFiles = [] 

1240 

1241 self._vhdlversion = VHDLVersion.VHDL2008 

1242 

1243 self._vhdlLibrary = None 

1244 self._vhdlLibraries = {} 

1245 

1246 self._testcase = None 

1247 self._testsuite = None 

1248 self._testsuites = {} 

1249 self._options = {} 

1250 

1251 self._build = None 

1252 self._builds = {} 

1253 

1254 @readonly 

1255 def Processor(self): # -> "Tk": 

1256 """ 

1257 Read-only property to access the TCL processor (:attr:`_processor`). 

1258 

1259 :returns: The TCL processor. 

1260 """ 

1261 return self._processor 

1262 

1263 @property 

1264 def LastException(self) -> Nullable[Exception]: 

1265 """ 

1266 Property to access the last seen Python exception (:attr:`_lastException`). 

1267 

1268 :returns: The last seen Python exception. This might return ``None``. 

1269 """ 

1270 lastException = self._lastException 

1271 self._lastException = None 

1272 return lastException 

1273 

1274 @LastException.setter 

1275 def LastException(self, value: Exception) -> None: 

1276 self._lastException = value 

1277 

1278 @readonly 

1279 def WorkingDirectory(self) -> Path: 

1280 """ 

1281 Read-only property to access the working directory (:attr:`_workingDirectory`). 

1282 

1283 :returns: The working directory. 

1284 """ 

1285 return self._workingDirectory 

1286 

1287 @readonly 

1288 def CurrentDirectory(self) -> Path: 

1289 """ 

1290 Read-only property to access the current directory (:attr:`_currentDirectory`). 

1291 

1292 The current directory is a virtual working directory used while processing ``*.pro`` files. 

1293 

1294 :returns: The current directory. 

1295 """ 

1296 return self._currentDirectory 

1297 

1298 @property 

1299 def VHDLVersion(self) -> VHDLVersion: 

1300 """ 

1301 Property to access the VHDL language revision (:attr:`_vhdlVersion`). 

1302 

1303 :returns: The currently set VHDL revision. 

1304 """ 

1305 return self._vhdlversion 

1306 

1307 @VHDLVersion.setter 

1308 def VHDLVersion(self, value: VHDLVersion) -> None: 

1309 self._vhdlversion = value 

1310 

1311 @readonly 

1312 def IncludedFiles(self) -> List[Path]: 

1313 """ 

1314 Read-only property to access list of included ``*.pro`` files (:attr:`_includedFiles`). 

1315 

1316 :returns: The list of loaded files. 

1317 """ 

1318 return self._includedFiles 

1319 

1320 @readonly 

1321 def VHDLLibrary(self) -> VHDLLibrary: 

1322 """ 

1323 Read-only property to access the currently active VHDL library (:attr:`_vhdlLibrary`). 

1324 

1325 :returns: The active VHDL libraries. 

1326 """ 

1327 return self._vhdlLibrary 

1328 

1329 @readonly 

1330 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]: 

1331 """ 

1332 Read-only property to access the dictionary of known VHDL libraries (:attr:`_vhdlLibraries`). 

1333 

1334 :returns: The dictionary of VHDL libraries. 

1335 """ 

1336 return self._vhdlLibraries 

1337 

1338 @readonly 

1339 def Testsuite(self) -> Testsuite: 

1340 """ 

1341 Read-only property to access the currently active OSVVM testsuite (:attr:`_testsuite`). 

1342 

1343 :returns: The active OSVVM testsuite. 

1344 """ 

1345 return self._testsuite 

1346 

1347 @readonly 

1348 def Testsuites(self) -> Dict[str, Testsuite]: 

1349 """ 

1350 Read-only property to access the dictionary of known OSVVM testsuites (:attr:`_testsuites`). 

1351 

1352 :returns: The dictionary of OSVVM testsuites. 

1353 """ 

1354 return self._testsuites 

1355 

1356 @readonly 

1357 def TestCase(self) -> Testcase: 

1358 """ 

1359 Read-only property to access the currently active OSVVM testcase (:attr:`_testcase`). 

1360 

1361 :returns: The active OSVVM testcase. 

1362 """ 

1363 return self._testcase 

1364 

1365 @readonly 

1366 def Build(self) -> Build: 

1367 """ 

1368 Read-only property to access the currently active OSVVM build (:attr:`_build`). 

1369 

1370 :returns: The active OSVVM build. 

1371 """ 

1372 return self._build 

1373 

1374 @readonly 

1375 def Builds(self) -> Dict[str, Build]: 

1376 """ 

1377 Read-only property to access the dictionary of known OSVVM builds (:attr:`_build`). 

1378 

1379 :returns: The dictionary of OSVVM builds. 

1380 """ 

1381 return self._builds 

1382 

1383 def ToProject(self, projectName: str) -> Project: 

1384 """ 

1385 Convert the context to an OSVVM project. 

1386 

1387 :param projectName: Name of the project. 

1388 :returns: OSVVM project. 

1389 """ 

1390 return Project(projectName, self._builds) 

1391 

1392 def RaiseException(self, ex: Exception, cause: Nullable[Exception] = None) -> NoReturn: 

1393 """ 

1394 Raise an exception, but keep a reference to the exception object in the TCL execution context. 

1395 

1396 :param ex: Exception to be raised. 

1397 """ 

1398 if cause is not None: 

1399 ex.__cause__ = cause 

1400 

1401 self._lastException = ex 

1402 raise ex 

1403 

1404 def BeginBuild(self, buildName: str) -> Build: 

1405 """ 

1406 Begin a new build context within the overall TCL execution context. 

1407 

1408 :param buildName: Name of the new build. 

1409 :returns: Currently active OSVVM build object. 

1410 :raises OSVVMException: When a VHDL library has been created outside a build context. 

1411 :raises OSVVMException: When a OSVVM testsuite has been created outside a build context. 

1412 """ 

1413 if len(self._vhdlLibraries) > 0: 1413 ↛ 1414line 1413 didn't jump to line 1414 because the condition on line 1413 was never true

1414 ex = OSVVMException(f"VHDL libraries have been created outside of an OSVVM build script.") 

1415 ex.add_note(f"TCL command 'library' has been called before 'build'.") 

1416 raise ex 

1417 if len(self._testsuites) > 0: 1417 ↛ 1418line 1417 didn't jump to line 1418 because the condition on line 1417 was never true

1418 ex = OSVVMException(f"Testsuites have been created outside of an OSVVM build script.") 

1419 ex.add_note(f"TCL command 'TestSuite' has been called before 'build'.") 

1420 raise ex 

1421 

1422 build = Build(buildName) 

1423 build._vhdlLibraries = self._vhdlLibraries 

1424 build._testsuites = self._testsuites 

1425 

1426 self._build = build 

1427 self._builds[buildName] = build 

1428 

1429 return build 

1430 

1431 def EndBuild(self) -> Build: 

1432 """ 

1433 Finalize the currently active build context. 

1434 

1435 The TCL execution context is cleaned up: partially reset the context and initialize some fields with new data 

1436 structures. 

1437 

1438 :returns: The OSVVM build object. 

1439 """ 

1440 build = self._build 

1441 

1442 self._vhdlLibrary = None 

1443 self._vhdlLibraries = {} 

1444 self._testcase = None 

1445 self._testsuite = None 

1446 self._testsuites = {} 

1447 self._build = None 

1448 

1449 # TODO: should this be handled in LoadBuildFile ? 

1450 self._currentDirectory = self._workingDirectory 

1451 

1452 return build 

1453 

1454 def IncludeFile(self, proFileOrBuildDirectory: Path) -> Path: 

1455 """ 

1456 Include a specific ``*.pro`` file or the ``*.pro`` file from an OSVVM build directory. 

1457 

1458 .. hint:: 

1459 

1460 An OSVVM build directory is a directory: 

1461 

1462 * containing a ``build.pro`` file (new style), or 

1463 * containing a ``*.pro`` file with the same name as the directory its contained in (old style). 

1464 

1465 :param proFileOrBuildDirectory: The path to the ``*.pro`` file or directory containing a ``*.pro`` file. |br| 

1466 Only relative paths are supported. 

1467 :returns: The resolved path to the found ``*.pro`` file. 

1468 :raises TypeError: When parameter 'proFileOrBuildDirectory' is not of type :class:`~pathlib.Path`. 

1469 :raises OSVVMException: When parameter 'proFileOrBuildDirectory' contains an absolut path. 

1470 :raises OSVVMException: When the resolved path doesn't reference to a ``*.pro`` file. 

1471 :raises OSVVMException: When the resolved path isn't an OSVVM build directory. 

1472 :raises OSVVMException: When the resolved path neither references a ``*.pro`` file nor an OSVVM build 

1473 directory. 

1474 """ 

1475 if not isinstance(proFileOrBuildDirectory, Path): # pragma: no cover 

1476 ex = TypeError(f"Parameter 'proFileOrBuildDirectory' is not a Path.") 

1477 ex.add_note(f"Got type '{getFullyQualifiedName(proFileOrBuildDirectory)}'.") 

1478 self.RaiseException(ex) 

1479 

1480 if proFileOrBuildDirectory.is_absolute(): 1480 ↛ 1481line 1480 didn't jump to line 1481 because the condition on line 1480 was never true

1481 ex = OSVVMException(f"Absolute path '{proFileOrBuildDirectory}' not supported.") 

1482 self.RaiseException(ex) 

1483 

1484 path = (self._currentDirectory / proFileOrBuildDirectory).resolve() 

1485 if path.is_file(): 

1486 if path.suffix == ".pro": 1486 ↛ 1490line 1486 didn't jump to line 1490 because the condition on line 1486 was always true

1487 self._currentDirectory = path.parent.relative_to(self._workingDirectory, walk_up=True) 

1488 proFile = self._currentDirectory / path.name 

1489 else: 

1490 self.RaiseException(OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a *.pro file.")) 

1491 elif path.is_dir(): 

1492 self._currentDirectory = path 

1493 proFile = path / "build.pro" 

1494 if not proFile.exists(): 

1495 proFile = path / f"{path.name}.pro" 

1496 if not proFile.exists(): # pragma: no cover 

1497 ex = OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a build directory.") 

1498 ex.__cause__ = FileNotFoundError(path / "build.pro") 

1499 self.RaiseException(ex) 

1500 else: # pragma: no cover 

1501 self.RaiseException(OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a *.pro file or build directory.")) 

1502 

1503 self._includedFiles.append(proFile) 

1504 return proFile 

1505 

1506 def EvaluateFile(self, proFile: Path) -> None: 

1507 """ 

1508 Evaluate a ``*.pro`` file. 

1509 

1510 :param proFile: OSVVM ``*.pro`` file to process. 

1511 """ 

1512 self._processor.EvaluateProFile(proFile) 

1513 

1514 def SetLibrary(self, name: str) -> None: 

1515 """ 

1516 Set or create the currently active VHDL library. 

1517 

1518 If the VHDL library isn't known in the current context, create a new VHDL library with the given name. 

1519 

1520 :param name: Name of the VHDL library. 

1521 :returns: Activated VHDL library. 

1522 """ 

1523 try: 

1524 self._vhdlLibrary = self._vhdlLibraries[name] 

1525 except KeyError: 

1526 self._vhdlLibrary = VHDLLibrary(name, build=self._build) 

1527 self._vhdlLibraries[name] = self._vhdlLibrary 

1528 

1529 def AddVHDLFile(self, vhdlFile: VHDLSourceFile) -> None: 

1530 """ 

1531 Add a VHDL source file to the currently active VHDL library. 

1532 

1533 The VHDL source file's VHDL revision is derived from currently active VHDL revision of the TCL execution context. 

1534 

1535 .. note:: 

1536 

1537 If there is no active VHDL library in the context, a new VHDL library named ``default`` is created. 

1538 

1539 :param vhdlFile: VHDL source file to be added. 

1540 """ 

1541 if self._vhdlLibrary is None: 

1542 self.SetLibrary("default") 

1543 

1544 vhdlFile.VHDLVersion = self._vhdlversion 

1545 self._vhdlLibrary.AddFile(vhdlFile) 

1546 

1547 def SetTestsuite(self, testsuiteName: str) -> None: 

1548 """ 

1549 Set or create the currently active OSVVM testsuite. 

1550 

1551 If the testsuite isn't known in the current context, create a new testsuite with the given name. 

1552 

1553 :param testsuiteName: Name of the OSVVM testsuite. 

1554 :returns: Activated OSVVM testsuite. 

1555 """ 

1556 try: 

1557 self._testsuite = self._testsuites[testsuiteName] 

1558 except KeyError: 

1559 self._testsuite = Testsuite(testsuiteName) 

1560 self._testsuites[testsuiteName] = self._testsuite 

1561 

1562 # TODO: should this be called differently then Add***, because it doesn't take an object, but a new and creates a new object. 

1563 def AddTestcase(self, testName: str) -> TestCase: 

1564 """ 

1565 Create a new testcase and add to the currently active OSVVM testsuite. 

1566 

1567 .. note:: 

1568 

1569 If there is no active OSVVM testsuite in the context, a new testsuite named ``default`` is created. 

1570 

1571 :param testName: Name of the testcase. 

1572 :returns: The created OSVVM testcase object. 

1573 """ 

1574 if self._testsuite is None: 

1575 self.SetTestsuite("default") 

1576 

1577 self._testcase = Testcase(testName) 

1578 self._testsuite._testcases[testName] = self._testcase 

1579 

1580 return self._testcase 

1581 

1582 def SetTestcaseToplevel(self, toplevel: str) -> TestCase: 

1583 """ 

1584 Set the testcase's toplevel entity or configuration name. 

1585 

1586 :param toplevel: Name of the toplevel entity or configuration. 

1587 :returns: The currently active OSVVM testcase. 

1588 :raises OSVVMException: When there is no active OSVVM testcase. 

1589 """ 

1590 if self._testcase is None: 1590 ↛ 1591line 1590 didn't jump to line 1591 because the condition on line 1590 was never true

1591 self.RaiseException(OSVVMException("Can't set testcase toplevel, because no testcase was setup.")) 

1592 

1593 self._testcase.SetToplevel(toplevel) 

1594 

1595 return self._testcase 

1596 

1597 # TODO: this this an add operation or a register operation? 

1598 def AddOption(self, option: Option) -> int: 

1599 """ 

1600 Register a new option and return a unique ID. 

1601 

1602 .. hint:: 

1603 

1604 TCL can't pass complex Python objects through the TCL layer back to Python. Therefore, complex objects like 

1605 options are registered in a dictionary and a unique ID (integer) is returned. Back in Python, this ID can be 

1606 converted back to the Python object. 

1607 

1608 This unique ID is based on :func:`id`. 

1609 

1610 :param option: Option to register. 

1611 :returns: Unique option ID. 

1612 """ 

1613 optionID = id(option) 

1614 self._options[optionID] = option 

1615 

1616 return optionID 

1617 

1618 

1619osvvmContext: Context = Context() 

1620""" 

1621Global OSVVM processing context. 

1622 

1623:type: Context 

1624"""