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

572 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-12 23:17 +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 return self._lastException 

1271 

1272 @LastException.setter 

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

1274 self._lastException = value 

1275 

1276 @readonly 

1277 def WorkingDirectory(self) -> Path: 

1278 """ 

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

1280 

1281 :returns: The working directory. 

1282 """ 

1283 return self._workingDirectory 

1284 

1285 @readonly 

1286 def CurrentDirectory(self) -> Path: 

1287 """ 

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

1289 

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

1291 

1292 :returns: The current directory. 

1293 """ 

1294 return self._currentDirectory 

1295 

1296 @property 

1297 def VHDLVersion(self) -> VHDLVersion: 

1298 """ 

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

1300 

1301 :returns: The currently set VHDL revision. 

1302 """ 

1303 return self._vhdlversion 

1304 

1305 @VHDLVersion.setter 

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

1307 self._vhdlversion = value 

1308 

1309 @readonly 

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

1311 """ 

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

1313 

1314 :returns: The list of loaded files. 

1315 """ 

1316 return self._includedFiles 

1317 

1318 @readonly 

1319 def VHDLLibrary(self) -> VHDLLibrary: 

1320 """ 

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

1322 

1323 :returns: The active VHDL libraries. 

1324 """ 

1325 return self._vhdlLibrary 

1326 

1327 @readonly 

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

1329 """ 

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

1331 

1332 :returns: The dictionary of VHDL libraries. 

1333 """ 

1334 return self._vhdlLibraries 

1335 

1336 @readonly 

1337 def Testsuite(self) -> Testsuite: 

1338 """ 

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

1340 

1341 :returns: The active OSVVM testsuite. 

1342 """ 

1343 return self._testsuite 

1344 

1345 @readonly 

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

1347 """ 

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

1349 

1350 :returns: The dictionary of OSVVM testsuites. 

1351 """ 

1352 return self._testsuites 

1353 

1354 @readonly 

1355 def TestCase(self) -> Testcase: 

1356 """ 

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

1358 

1359 :returns: The active OSVVM testcase. 

1360 """ 

1361 return self._testcase 

1362 

1363 @readonly 

1364 def Build(self) -> Build: 

1365 """ 

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

1367 

1368 :returns: The active OSVVM build. 

1369 """ 

1370 return self._build 

1371 

1372 @readonly 

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

1374 """ 

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

1376 

1377 :returns: The dictionary of OSVVM builds. 

1378 """ 

1379 return self._builds 

1380 

1381 def ClearLastException(self) -> Nullable[Exception]: 

1382 # """ 

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

1384 # 

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

1386 # """ 

1387 lastException = self._lastException 

1388 self._lastException = None 

1389 return lastException 

1390 

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

1392 """ 

1393 Convert the context to an OSVVM project. 

1394 

1395 :param projectName: Name of the project. 

1396 :returns: OSVVM project. 

1397 """ 

1398 return Project(projectName, self._builds) 

1399 

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

1401 """ 

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

1403 

1404 :param ex: Exception to be raised. 

1405 """ 

1406 if cause is not None: 

1407 ex.__cause__ = cause 

1408 

1409 self._lastException = ex 

1410 raise ex 

1411 

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

1413 """ 

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

1415 

1416 :param buildName: Name of the new build. 

1417 :returns: Currently active OSVVM build object. 

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

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

1420 """ 

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

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

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

1424 raise ex 

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

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

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

1428 raise ex 

1429 

1430 build = Build(buildName) 

1431 build._vhdlLibraries = self._vhdlLibraries 

1432 build._testsuites = self._testsuites 

1433 

1434 self._build = build 

1435 self._builds[buildName] = build 

1436 

1437 return build 

1438 

1439 def EndBuild(self) -> Build: 

1440 """ 

1441 Finalize the currently active build context. 

1442 

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

1444 structures. 

1445 

1446 :returns: The OSVVM build object. 

1447 """ 

1448 build = self._build 

1449 

1450 self._vhdlLibrary = None 

1451 self._vhdlLibraries = {} 

1452 self._testcase = None 

1453 self._testsuite = None 

1454 self._testsuites = {} 

1455 self._build = None 

1456 

1457 # TODO: should this be handled in LoadBuildFile ? 

1458 self._currentDirectory = self._workingDirectory 

1459 

1460 return build 

1461 

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

1463 """ 

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

1465 

1466 .. hint:: 

1467 

1468 An OSVVM build directory is a directory: 

1469 

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

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

1472 

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

1474 Only relative paths are supported. 

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

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

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

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

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

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

1481 directory. 

1482 """ 

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

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

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

1486 self.RaiseException(ex) 

1487 

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

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

1490 self.RaiseException(ex) 

1491 

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

1493 if path.is_file(): 

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

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

1496 proFile = self._currentDirectory / path.name 

1497 else: 

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

1499 elif path.is_dir(): 

1500 self._currentDirectory = path 

1501 proFile = path / "build.pro" 

1502 if not proFile.exists(): 

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

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

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

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

1507 self.RaiseException(ex) 

1508 else: # pragma: no cover 

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

1510 

1511 self._includedFiles.append(proFile) 

1512 return proFile 

1513 

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

1515 """ 

1516 Evaluate a ``*.pro`` file. 

1517 

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

1519 """ 

1520 self._processor.EvaluateProFile(proFile) 

1521 

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

1523 """ 

1524 Set or create the currently active VHDL library. 

1525 

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

1527 

1528 :param name: Name of the VHDL library. 

1529 :returns: Activated VHDL library. 

1530 """ 

1531 try: 

1532 self._vhdlLibrary = self._vhdlLibraries[name] 

1533 except KeyError: 

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

1535 self._vhdlLibraries[name] = self._vhdlLibrary 

1536 

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

1538 """ 

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

1540 

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

1542 

1543 .. note:: 

1544 

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

1546 

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

1548 """ 

1549 if self._vhdlLibrary is None: 

1550 self.SetLibrary("default") 

1551 

1552 vhdlFile.VHDLVersion = self._vhdlversion 

1553 self._vhdlLibrary.AddFile(vhdlFile) 

1554 

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

1556 """ 

1557 Set or create the currently active OSVVM testsuite. 

1558 

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

1560 

1561 :param testsuiteName: Name of the OSVVM testsuite. 

1562 :returns: Activated OSVVM testsuite. 

1563 """ 

1564 try: 

1565 self._testsuite = self._testsuites[testsuiteName] 

1566 except KeyError: 

1567 self._testsuite = Testsuite(testsuiteName) 

1568 self._testsuites[testsuiteName] = self._testsuite 

1569 

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

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

1572 """ 

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

1574 

1575 .. note:: 

1576 

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

1578 

1579 :param testName: Name of the testcase. 

1580 :returns: The created OSVVM testcase object. 

1581 """ 

1582 if self._testsuite is None: 

1583 self.SetTestsuite("default") 

1584 

1585 self._testcase = Testcase(testName) 

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

1587 

1588 return self._testcase 

1589 

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

1591 """ 

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

1593 

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

1595 :returns: The currently active OSVVM testcase. 

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

1597 """ 

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

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

1600 

1601 self._testcase.SetToplevel(toplevel) 

1602 

1603 return self._testcase 

1604 

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

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

1607 """ 

1608 Register a new option and return a unique ID. 

1609 

1610 .. hint:: 

1611 

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

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

1614 converted back to the Python object. 

1615 

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

1617 

1618 :param option: Option to register. 

1619 :returns: Unique option ID. 

1620 """ 

1621 optionID = id(option) 

1622 self._options[optionID] = option 

1623 

1624 return optionID 

1625 

1626 

1627osvvmContext: Context = Context() 

1628""" 

1629Global OSVVM processing context. 

1630 

1631:type: Context 

1632"""