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

557 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-28 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-2025 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# 

31from pathlib import Path 

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

33 

34from pyTooling.Common import getFullyQualifiedName 

35from pyTooling.Decorators import readonly, export 

36from pyTooling.MetaClasses import ExtendedType 

37from pyVHDLModel import VHDLVersion 

38 

39from pyEDAA.OSVVM import OSVVMException 

40 

41 

42__all__ = ["osvvmContext"] 

43 

44 

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

46 

47 

48@export 

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

50 _parent: Nullable[_ParentType] 

51 

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

53 self._parent = parent 

54 

55 @readonly 

56 def Parent(self) -> _ParentType: 

57 return self._parent 

58 

59 

60@export 

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

62 _name: str 

63 

64 def __init__( 

65 self, 

66 name: str, 

67 parent: Nullable[_ParentType] = None 

68 ) -> None: 

69 super().__init__(parent) 

70 

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

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

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

74 raise ex 

75 

76 self._name = name 

77 

78 @readonly 

79 def Name(self) -> str: 

80 return self._name 

81 

82 def __repr__(self) -> str: 

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

84 

85 

86@export 

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

88 pass 

89 

90 

91@export 

92class NoNullRangeWarning(Option): 

93 def __init__(self) -> None: 

94 super().__init__() 

95 

96 def __repr__(self) -> str: 

97 return "NoNullRangeWarning" 

98 

99 

100@export 

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

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

103 

104 _path: Path 

105 

106 def __init__( 

107 self, 

108 path: Path, 

109 parent: Nullable[Base] = None 

110 ) -> None: 

111 super().__init__(parent) 

112 

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

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

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

116 raise ex 

117 

118 self._path = path 

119 

120 @readonly 

121 def Path(self) -> Path: 

122 """ 

123 Read-only property to access the path to the sourcefile. 

124 

125 :returns: The sourcefile's path. 

126 """ 

127 return self._path 

128 

129 def __repr__(self) -> str: 

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

131 

132 

133@export 

134class XDCConstraintFile(SourceFile): 

135 _scopeToRef: str 

136 _scopeToCell: str 

137 

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

139 super().__init__(path) 

140 

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

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

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

144 raise ex 

145 

146 self._scopeToRef = scopeToRef 

147 

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

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

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

151 raise ex 

152 

153 self._scopeToCell = scopeToCell 

154 

155 def __repr__(self) -> str: 

156 properties = [] 

157 if self._scopeToRef is not None: 

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

159 if self._scopeToCell is not None: 

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

161 

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

163 

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

165 

166 

167@export 

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

169 _vhdlVersion: VHDLVersion 

170 _noNullRangeWarning: Nullable[bool] 

171 _associatedFiles: List[XDCConstraintFile] 

172 

173 def __init__( 

174 self, 

175 path: Path, 

176 vhdlVersion: VHDLVersion = VHDLVersion.VHDL2008, 

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

178 noNullRangeWarning: Nullable[bool] = None, 

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

180 ) -> None: 

181 if vhdlLibrary is None: 

182 super().__init__(path) 

183 elif isinstance(vhdlLibrary, VHDLLibrary): 

184 super().__init__(path, vhdlLibrary) 

185 vhdlLibrary._files.append(self) 

186 else: # pragma: no cover 

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

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

189 raise ex 

190 

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

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

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

194 raise ex 

195 

196 self._vhdlVersion = vhdlVersion 

197 

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

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

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

201 raise ex 

202 

203 self._noNullRangeWarning = noNullRangeWarning 

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

205 

206 @readonly 

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

208 return self._parent 

209 

210 @property 

211 def VHDLVersion(self) -> VHDLVersion: 

212 return self._vhdlVersion 

213 

214 @VHDLVersion.setter 

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

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

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

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

219 raise ex 

220 

221 self._vhdlVersion = value 

222 

223 @property 

224 def NoNullRangeWarning(self) -> bool: 

225 return self._noNullRangeWarning 

226 

227 @NoNullRangeWarning.setter 

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

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

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

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

232 raise ex 

233 

234 self._noNullRangeWarning = value 

235 

236 @property 

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

238 return self._associatedFiles 

239 

240 def __repr__(self) -> str: 

241 options = "" 

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

243 options += f", NoNullRangeWarning" 

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

245 

246 

247@export 

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

249 """A VHDL library collecting multiple VHDL files containing VHDL design units.""" 

250 

251 _files: List[VHDLSourceFile] 

252 

253 def __init__( 

254 self, 

255 name: str, 

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

257 build: Nullable["Build"] = None 

258 ) -> None: 

259 if build is None: 

260 super().__init__(name, None) 

261 elif isinstance(build, Build): 

262 super().__init__(name, build) 

263 build._vhdlLibraries[name] = self 

264 else: # pragma: no cover 

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

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

267 raise ex 

268 

269 self._files = [] 

270 if vhdlFiles is None: 

271 pass 

272 elif isinstance(vhdlFiles, Iterable): 

273 for vhdlFile in vhdlFiles: 

274 vhdlFile._parent = self 

275 self._files.append(vhdlFile) 

276 else: # pragma: no cover 

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

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

279 raise ex 

280 

281 @readonly 

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

283 return self._parent 

284 

285 @readonly 

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

287 return self._files 

288 

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

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

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

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

293 raise ex 

294 

295 file._parent = self 

296 self._files.append(file) 

297 

298 def __repr__(self) -> str: 

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

300 

301 

302@export 

303class GenericValue(Option): 

304 _name: str 

305 _value: str 

306 

307 def __init__( 

308 self, 

309 name: str, 

310 value: str 

311 ) -> None: 

312 super().__init__() 

313 

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

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

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

317 raise ex 

318 

319 self._name = name 

320 

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

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

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

324 raise ex 

325 

326 self._value = value 

327 

328 @readonly 

329 def Name(self) -> str: 

330 return self._name 

331 

332 @readonly 

333 def Value(self) -> str: 

334 return self._value 

335 

336 def __repr__(self) -> str: 

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

338 

339 

340@export 

341class ConstraintFile(Option): 

342 _path: Path 

343 _scopeToRef: Nullable[str] 

344 _scopeToCell: Nullable[str] 

345 

346 def __init__( 

347 self, 

348 path: Path, 

349 scopeToRef: Nullable[str] = None, 

350 scopeToCell: Nullable[str] = None 

351 ) -> None: 

352 super().__init__() 

353 

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

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

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

357 raise ex 

358 

359 self._path = path 

360 

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

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

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

364 raise ex 

365 

366 self._scopeToRef = scopeToRef 

367 

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

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

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

371 raise ex 

372 

373 self._scopeToCell = scopeToCell 

374 

375 @readonly 

376 def Path(self) -> Path: 

377 return self._path 

378 

379 @readonly 

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

381 return self._scopeToRef 

382 

383 @readonly 

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

385 return self._scopeToCell 

386 

387 def __repr__(self) -> str: 

388 properties = [] 

389 if self._scopeToRef is not None: 

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

391 if self._scopeToCell is not None: 

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

393 

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

395 

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

397 

398 

399@export 

400class ScopeToRef(Option): 

401 _reference: str 

402 

403 def __init__( 

404 self, 

405 reference: str 

406 ) -> None: 

407 super().__init__() 

408 

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

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

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

412 raise ex 

413 

414 self._reference = reference 

415 

416 @readonly 

417 def Reference(self) -> str: 

418 return self._reference 

419 

420 def __repr__(self) -> str: 

421 return f"{self._reference}" 

422 

423 

424@export 

425class ScopeToCell(Option): 

426 _cell: str 

427 

428 def __init__( 

429 self, 

430 cell: str 

431 ) -> None: 

432 super().__init__() 

433 

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

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

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

437 raise ex 

438 

439 self._cell = cell 

440 

441 @readonly 

442 def Cell(self) -> str: 

443 return self._cell 

444 

445 def __repr__(self) -> str: 

446 return f"{self._cell}" 

447 

448 

449@export 

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

451 _toplevelName: Nullable[str] 

452 _generics: Dict[str, str] 

453 

454 def __init__( 

455 self, 

456 name: str, 

457 toplevelName: Nullable[str] = None, 

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

459 testsuite: Nullable["Testsuite"] = None 

460 ) -> None: 

461 if testsuite is None: 

462 super().__init__(name, None) 

463 elif isinstance(testsuite, Testsuite): 

464 super().__init__(name, testsuite) 

465 testsuite._testcases[name] = self 

466 else: # pragma: no cover 

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

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

469 raise ex 

470 

471 if not (toplevelName is None or isinstance(toplevelName, str)): # pragma: no cover 

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

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

474 raise ex 

475 

476 self._toplevelName = toplevelName 

477 

478 self._generics = {} 

479 if generics is None: 

480 pass 

481 elif isinstance(generics, Mapping): 

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

483 self._generics[key] = value 

484 elif isinstance(generics, Iterable): 

485 for item in generics: 

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

487 else: # pragma: no cover 

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

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

490 raise ex 

491 

492 @readonly 

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

494 return self._parent 

495 

496 @readonly 

497 def ToplevelName(self) -> str: 

498 return self._toplevelName 

499 

500 @readonly 

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

502 return self._generics 

503 

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

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

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

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

508 raise ex 

509 

510 self._toplevelName = toplevelName 

511 

512 def AddGeneric(self, genericValue: GenericValue): 

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

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

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

516 raise ex 

517 

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

519 

520 def __repr__(self) -> str: 

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

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

523 

524 

525@export 

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

527 _testcases: Dict[str, Testcase] 

528 

529 def __init__( 

530 self, 

531 name: str, 

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

533 build: Nullable["Build"] = None 

534 ) -> None: 

535 if build is None: 

536 super().__init__(name, None) 

537 elif isinstance(build, Build): 

538 super().__init__(name, build) 

539 build._testsuites[name] = self 

540 else: # pragma: no cover 

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

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

543 raise ex 

544 

545 self._testcases = {} 

546 if testcases is None: 

547 pass 

548 elif isinstance(testcases, Mapping): 

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

550 value._parent = self 

551 self._testcases[key] = value 

552 elif isinstance(testcases, Iterable): 

553 for item in testcases: 

554 item._parent = self 

555 self._testcases[item._name] = item 

556 else: # pragma: no cover 

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

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

559 raise ex 

560 

561 @readonly 

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

563 return self._parent 

564 

565 @readonly 

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

567 return self._testcases 

568 

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

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

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

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

573 raise ex 

574 

575 testcase._parent = self 

576 self._testcases[testcase._name] = testcase 

577 

578 def __repr__(self) -> str: 

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

580 

581 

582@export 

583class BuildName(Option): 

584 _name: str 

585 

586 def __init__( 

587 self, 

588 name: str, 

589 ) -> None: 

590 super().__init__() 

591 

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

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

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

595 raise ex 

596 

597 self._name = name 

598 

599 @readonly 

600 def Name(self) -> str: 

601 return self._name 

602 

603 def __repr__(self) -> str: 

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

605 

606 

607@export 

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

609 _includedFiles: List[Path] 

610 _vhdlLibraries: Dict[str, VHDLLibrary] 

611 _testsuites: Dict[str, Testsuite] 

612 

613 def __init__( 

614 self, 

615 name: str, 

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

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

618 project: Nullable[Base] = None 

619 ) -> None: 

620 if project is None: 

621 super().__init__(name, None) 

622 elif isinstance(project, Project): 

623 super().__init__(name, project) 

624 project._builds[name] = self 

625 else: # pragma: no cover 

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

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

628 raise ex 

629 

630 self._includedFiles = [] 

631 self._vhdlLibraries = {} 

632 if vhdlLibraries is None: 

633 pass 

634 elif isinstance(vhdlLibraries, Mapping): 

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

636 value._parent = self 

637 self._vhdlLibraries[key] = value 

638 elif isinstance(vhdlLibraries, Iterable): 

639 for item in vhdlLibraries: 

640 item._parent = self 

641 self._vhdlLibraries[item._name] = item 

642 else: # pragma: no cover 

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

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

645 raise ex 

646 

647 self._testsuites = {} 

648 if testsuites is None: 

649 pass 

650 elif isinstance(testsuites, Mapping): 

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

652 value._parent = self 

653 self._testsuites[key] = value 

654 elif isinstance(testsuites, Iterable): 

655 for item in testsuites: 

656 item._parent = self 

657 self._testsuites[item._name] = item 

658 else: # pragma: no cover 

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

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

661 raise ex 

662 

663 @readonly 

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

665 return self._parent 

666 

667 @readonly 

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

669 return (file for file in self._includedFiles) 

670 

671 @readonly 

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

673 return self._vhdlLibraries 

674 

675 @readonly 

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

677 return self._testsuites 

678 

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

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

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

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

683 raise ex 

684 

685 vhdlLibrary._parent = self 

686 self._vhdlLibraries[vhdlLibrary._name] = vhdlLibrary 

687 

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

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

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

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

692 raise ex 

693 

694 testsuite._parent = self 

695 self._testsuites[testsuite._name] = testsuite 

696 

697 def __repr__(self) -> str: 

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

699 

700 

701@export 

702class Project(Named[None]): 

703 _builds: Dict[str, Build] 

704 

705 def __init__( 

706 self, 

707 name: str, 

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

709 ) -> None: 

710 super().__init__(name, None) 

711 

712 self._builds = {} 

713 if builds is None: 

714 pass 

715 elif isinstance(builds, Mapping): 

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

717 value._parent = self 

718 self._builds[key] = value 

719 elif isinstance(builds, Iterable): 

720 for item in builds: 

721 item._parent = self 

722 self._builds[item._name] = item 

723 else: # pragma: no cover 

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

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

726 raise ex 

727 

728 @readonly 

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

730 return self._builds 

731 

732 @readonly 

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

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

735 yield from build.IncludedFiles 

736 

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

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

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

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

741 raise ex 

742 

743 build._parent = self 

744 self._builds[build._name] = build 

745 

746 def __repr__(self) -> str: 

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

748 

749 

750@export 

751class Context(Base): 

752 # _tcl: TclEnvironment 

753 

754 _processor: "OsvvmProFileProcessor" 

755 _lastException: Exception 

756 

757 _workingDirectory: Path 

758 _currentDirectory: Path 

759 _includedFiles: List[Path] 

760 

761 _vhdlversion: VHDLVersion 

762 

763 _vhdlLibraries: Dict[str, VHDLLibrary] 

764 _vhdlLibrary: Nullable[VHDLLibrary] 

765 

766 _testsuites: Dict[str, Testsuite] 

767 _testsuite: Nullable[Testsuite] 

768 _testcase: Nullable[Testcase] 

769 _options: Dict[int, Option] 

770 

771 _builds: Dict[str, Build] 

772 _build: Nullable[Build] 

773 

774 def __init__(self) -> None: 

775 super().__init__() 

776 

777 self._processor = None 

778 self._lastException = None 

779 

780 self._workingDirectory = Path.cwd() 

781 self._currentDirectory = self._workingDirectory 

782 self._includedFiles = [] 

783 

784 self._vhdlversion = VHDLVersion.VHDL2008 

785 

786 self._vhdlLibrary = None 

787 self._vhdlLibraries = {} 

788 

789 self._testcase = None 

790 self._testsuite = None 

791 self._testsuites = {} 

792 self._options = {} 

793 

794 self._build = None 

795 self._builds = {} 

796 

797 def Clear(self) -> None: 

798 self._processor = None 

799 self._lastException = None 

800 

801 self._workingDirectory = Path.cwd() 

802 self._currentDirectory = self._workingDirectory 

803 self._includedFiles = [] 

804 

805 self._vhdlversion = VHDLVersion.VHDL2008 

806 

807 self._vhdlLibrary = None 

808 self._vhdlLibraries = {} 

809 

810 self._testcase = None 

811 self._testsuite = None 

812 self._testsuites = {} 

813 self._options = {} 

814 

815 self._build = None 

816 self._builds = {} 

817 

818 @readonly 

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

820 return self._processor 

821 

822 @property 

823 def LastException(self) -> Exception: 

824 lastException = self._lastException 

825 self._lastException = None 

826 return lastException 

827 

828 @LastException.setter 

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

830 self._lastException = value 

831 

832 @readonly 

833 def WorkingDirectory(self) -> Path: 

834 return self._workingDirectory 

835 

836 @readonly 

837 def CurrentDirectory(self) -> Path: 

838 return self._currentDirectory 

839 

840 @property 

841 def VHDLVersion(self) -> VHDLVersion: 

842 return self._vhdlversion 

843 

844 @VHDLVersion.setter 

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

846 self._vhdlversion = value 

847 

848 @readonly 

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

850 return self._includedFiles 

851 

852 @readonly 

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

854 return self._vhdlLibraries 

855 

856 @readonly 

857 def VHDLLibrary(self) -> VHDLLibrary: 

858 return self._vhdlLibrary 

859 

860 @readonly 

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

862 return self._testsuites 

863 

864 @readonly 

865 def Testsuite(self) -> Testsuite: 

866 return self._testsuite 

867 

868 @readonly 

869 def TestCase(self) -> Testcase: 

870 return self._testcase 

871 

872 @readonly 

873 def Build(self) -> Build: 

874 return self._build 

875 

876 @readonly 

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

878 return self._builds 

879 

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

881 project = Project(projectName, self._builds) 

882 

883 return project 

884 

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

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

887 raise OSVVMException(f"VHDL libraries have been created outside of an OSVVM build script.") 

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

889 raise OSVVMException(f"Testsuites have been created outside of an OSVVM build script.") 

890 

891 build = Build(buildName) 

892 build._vhdlLibraries = self._vhdlLibraries 

893 build._testsuites = self._testsuites 

894 

895 self._build = build 

896 self._builds[buildName] = build 

897 

898 return build 

899 

900 def EndBuild(self) -> Build: 

901 build = self._build 

902 

903 self._vhdlLibrary = None 

904 self._vhdlLibraries = {} 

905 self._testcase = None 

906 self._testsuite = None 

907 self._testsuites = {} 

908 self._build = None 

909 

910 # TODO: should this be handled in LoadBuildFile ? 

911 self._currentDirectory = self._workingDirectory 

912 

913 return build 

914 

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

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

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

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

919 self._lastException = ex 

920 raise ex 

921 

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

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

924 self._lastException = ex 

925 raise ex 

926 

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

928 if path.is_file(): 

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

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

931 proFile = self._currentDirectory / path.name 

932 else: 

933 ex = OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a *.pro file.") 

934 self._lastException = ex 

935 raise ex 

936 elif path.is_dir(): 

937 self._currentDirectory = path 

938 proFile = path / "build.pro" 

939 if not proFile.exists(): 

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

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

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

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

944 self._lastException = ex 

945 raise ex 

946 else: # pragma: no cover 

947 ex = OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a *.pro file or build directory.") 

948 self._lastException = ex 

949 raise ex 

950 

951 self._includedFiles.append(proFile) 

952 return proFile 

953 

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

955 self._processor.EvaluateProFile(proFile) 

956 

957 def SetLibrary(self, name: str): 

958 try: 

959 self._vhdlLibrary = self._vhdlLibraries[name] 

960 except KeyError: 

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

962 self._vhdlLibraries[name] = self._vhdlLibrary 

963 

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

965 if self._vhdlLibrary is None: 

966 self.SetLibrary("default") 

967 

968 vhdlFile.VHDLVersion = self._vhdlversion 

969 self._vhdlLibrary.AddFile(vhdlFile) 

970 

971 def SetTestsuite(self, testsuiteName: str): 

972 try: 

973 self._testsuite = self._testsuites[testsuiteName] 

974 except KeyError: 

975 self._testsuite = Testsuite(testsuiteName) 

976 self._testsuites[testsuiteName] = self._testsuite 

977 

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

979 if self._testsuite is None: 

980 self.SetTestsuite("default") 

981 

982 self._testcase = Testcase(testName) 

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

984 

985 return self._testcase 

986 

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

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

989 ex = OSVVMException("Can't set testcase toplevel, because no testcase was setup.") 

990 self._lastException = ex 

991 raise ex 

992 

993 self._testcase.SetToplevel(toplevel) 

994 

995 return self._testcase 

996 

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

998 optionID = id(option) 

999 self._options[optionID] = option 

1000 

1001 return optionID 

1002 

1003 

1004osvvmContext: Context = Context() 

1005""" 

1006Global OSVVM processing context. 

1007 

1008:type: Context 

1009"""