Coverage for pyEDAA/ProjectModel/__init__.py: 69%

1070 statements  

« prev     ^ index     » next       coverage.py v7.6.7, created at 2024-11-20 23:18 +0000

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

2# _____ ____ _ _ ____ _ _ __ __ _ _ # 

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

4# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | # 

5# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | # 

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

7# |_| |___/ |__/ # 

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

14# Copyright 2017-2024 Patrick Lehmann - Boetzingen, Germany # 

15# Copyright 2014-2016 Technische Universität Dresden - Germany, Chair of VLSI-Design, Diagnostics and Architecture # 

16# # 

17# Licensed under the Apache License, Version 2.0 (the "License"); # 

18# you may not use this file except in compliance with the License. # 

19# You may obtain a copy of the License at # 

20# # 

21# http://www.apache.org/licenses/LICENSE-2.0 # 

22# # 

23# Unless required by applicable law or agreed to in writing, software # 

24# distributed under the License is distributed on an "AS IS" BASIS, # 

25# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

26# See the License for the specific language governing permissions and # 

27# limitations under the License. # 

28# # 

29# SPDX-License-Identifier: Apache-2.0 # 

30# ==================================================================================================================== # 

31# 

32"""An abstract model of EDA tool projects.""" 

33__author__ = "Patrick Lehmann" 

34__email__ = "Paebbels@gmail.com" 

35__copyright__ = "2014-2024, Patrick Lehmann, Unai Martinez-Corral" 

36__license__ = "Apache License, Version 2.0" 

37__version__ = "0.5.0" 

38__keywords__ = ["eda project", "model", "abstract", "xilinx", "vivado", "osvvm", "file set", "file group", "test bench", "test harness"] 

39 

40from os.path import relpath as path_relpath 

41from pathlib import Path as pathlib_Path 

42from sys import version_info 

43from typing import Dict, Union, Optional as Nullable, List, Iterable, Generator, Tuple, Any as typing_Any, Type, Set, Any 

44 

45from pyTooling.Common import getFullyQualifiedName 

46from pyTooling.Decorators import export 

47from pyTooling.MetaClasses import ExtendedType 

48from pyTooling.Graph import Graph, Vertex 

49from pySVModel import SystemVerilogVersion 

50from pyVHDLModel import VHDLVersion 

51from pySystemRDLModel import SystemRDLVersion 

52 

53 

54@export 

55class Attribute(metaclass=ExtendedType): 

56 KEY: str 

57 VALUE_TYPE: typing_Any 

58 

59 @staticmethod 

60 def resolve(obj: typing_Any, key: Type['Attribute']): 

61 if isinstance(obj, File): 61 ↛ 63line 61 didn't jump to line 63 because the condition on line 61 was always true

62 return obj._fileSet[key] 

63 elif isinstance(obj, FileSet): 

64 return obj._design[key] 

65 elif isinstance(obj, Design): 

66 return obj._project[key] 

67 else: 

68 raise Exception("Resolution error") 

69 

70 

71@export 

72class FileType(ExtendedType): 

73 """ 

74 A :term:`meta-class` to construct *FileType* classes. 

75 

76 Modifications done by this meta-class: 

77 * Register all classes of type :class:`FileType` or derived variants in a class field :attr:`FileType.FileTypes` in this meta-class. 

78 """ 

79 

80 FileTypes: Dict[str, 'FileType'] = {} #: Dictionary of all classes of type :class:`FileType` or derived variants 

81 Any: 'FileType' 

82 

83 def __init__(cls, name: str, bases: Tuple[type, ...], dictionary: Dict[str, typing_Any], **kwargs): 

84 super().__init__(name, bases, dictionary, **kwargs) 

85 cls.Any = cls 

86 

87 def __new__(cls, className, baseClasses, classMembers: Dict, *args, **kwargs): 

88 fileType = super().__new__(cls, className, baseClasses, classMembers, *args, **kwargs) 

89 cls.FileTypes[className] = fileType 

90 return fileType 

91 

92 def __getattr__(cls, item) -> 'FileType': 

93 if item[:2] != "__" and item[-2:] != "__": 

94 return cls.FileTypes[item] 

95 else: 

96 return super().__getattribute__(item) 

97 

98 def __contains__(cls, item) -> bool: 

99 return issubclass(item, cls) 

100 

101 

102@export 

103class File(metaclass=FileType, slots=True): 

104 """ 

105 A :term:`File` represents a file in a design. This :term:`base-class` is used 

106 for all derived file classes. 

107 

108 A file can be created standalone and later associated to a fileset, design and 

109 project. Or a fileset, design and/or project can be associated immediately 

110 while creating a file. 

111 

112 :arg path: Relative or absolute path to the file. 

113 :arg project: Project the file is associated with. 

114 :arg design: Design the file is associated with. 

115 :arg fileSet: Fileset the file is associated with. 

116 """ 

117 

118 _path: pathlib_Path 

119 _fileType: 'FileType' 

120 _project: Nullable['Project'] 

121 _design: Nullable['Design'] 

122 _fileSet: Nullable['FileSet'] 

123 _attributes: Dict[Type[Attribute], typing_Any] 

124 

125 def __init__( 

126 self, 

127 path: pathlib_Path, 

128 project: Nullable["Project"] = None, 

129 design: Nullable["Design"] = None, 

130 fileSet: Nullable["FileSet"] = None 

131 ): 

132 self._fileType = getattr(FileTypes, self.__class__.__name__) 

133 self._path = path 

134 if project is not None: 

135 self._project = project 

136 self._design = design 

137 if fileSet is not None: 137 ↛ 138line 137 didn't jump to line 138 because the condition on line 137 was never true

138 self.FileSet = fileSet 

139 elif design is not None: 

140 self._project = design._project 

141 self._design = design 

142 self.FileSet = design.DefaultFileSet if fileSet is None else fileSet 

143 elif fileSet is not None: 

144 design = fileSet._design 

145 if design is not None: 

146 self._project = design._project 

147 else: 

148 self._project = None 

149 self._design = design 

150 self.FileSet = fileSet 

151 else: 

152 self._project = None 

153 self._design = None 

154 self._fileSet = None 

155 

156 self._attributes = {} 

157 self._registerAttributes() 

158 

159 def _registerAttributes(self) -> None: 

160 pass 

161 

162 @property 

163 def FileType(self) -> 'FileType': 

164 """Read-only property to return the file type of this file.""" 

165 return self._fileType 

166 

167 @property 

168 def Path(self) -> pathlib_Path: 

169 """Read-only property returning the path of this file.""" 

170 return self._path 

171 

172 # TODO: setter? 

173 

174 @property 

175 def ResolvedPath(self) -> pathlib_Path: 

176 """Read-only property returning the resolved path of this file.""" 

177 if self._path.is_absolute(): 177 ↛ 178line 177 didn't jump to line 178 because the condition on line 177 was never true

178 return self._path.resolve() 

179 elif self._fileSet is not None: 179 ↛ 189line 179 didn't jump to line 189 because the condition on line 179 was always true

180 path = (self._fileSet.ResolvedPath / self._path).resolve() 

181 

182 if path.is_absolute(): 182 ↛ 186line 182 didn't jump to line 186 because the condition on line 182 was always true

183 return path 

184 else: 

185 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath 

186 return pathlib_Path(path_relpath(path, pathlib_Path.cwd())) 

187 else: 

188 # TODO: message and exception type 

189 raise Exception("") 

190 

191 @property 

192 def Project(self) -> Nullable['Project']: 

193 """Property setting or returning the project this file is used in.""" 

194 return self._project 

195 

196 @Project.setter 

197 def Project(self, value: 'Project') -> None: 

198 self._project = value 

199 

200 if self._fileSet is None: 200 ↛ exitline 200 didn't return from function 'Project' because the condition on line 200 was always true

201 self._project.DefaultDesign.DefaultFileSet.AddFile(self) 

202 

203 @property 

204 def Design(self) -> Nullable['Design']: 

205 """Property setting or returning the design this file is used in.""" 

206 return self._design 

207 

208 @Design.setter 

209 def Design(self, value: 'Design') -> None: 

210 self._design = value 

211 

212 if self._fileSet is None: 212 ↛ 215line 212 didn't jump to line 215 because the condition on line 212 was always true

213 self._design.DefaultFileSet.AddFile(self) 

214 

215 if self._project is None: 215 ↛ 217line 215 didn't jump to line 217 because the condition on line 215 was always true

216 self._project = value._project 

217 elif self._project is not value._project: 

218 raise Exception("The design's project is not identical to the already assigned project.") 

219 

220 @property 

221 def FileSet(self) -> Nullable['FileSet']: 

222 """Property setting or returning the fileset this file is used in.""" 

223 return self._fileSet 

224 

225 @FileSet.setter 

226 def FileSet(self, value: 'FileSet') -> None: 

227 self._fileSet = value 

228 value._files.append(self) 

229 

230 def Validate(self) -> None: 

231 """Validate this file.""" 

232 if self._path is None: 232 ↛ 233line 232 didn't jump to line 233 because the condition on line 232 was never true

233 raise Exception("Validation: File has no path.") 

234 try: 

235 path = self.ResolvedPath 

236 except Exception as ex: 

237 raise Exception(f"Validation: File '{self._path}' could not compute resolved path.") from ex 

238 if not path.exists(): 238 ↛ 239line 238 didn't jump to line 239 because the condition on line 238 was never true

239 raise Exception(f"Validation: File '{self._path}' (={path}) does not exist.") 

240 if not path.is_file(): 240 ↛ 241line 240 didn't jump to line 241 because the condition on line 240 was never true

241 raise Exception(f"Validation: File '{self._path}' (={path}) is not a file.") 

242 

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

244 raise Exception(f"Validation: File '{self._path}' has no fileset.") 

245 if self._design is None: 245 ↛ 246line 245 didn't jump to line 246 because the condition on line 245 was never true

246 raise Exception(f"Validation: File '{self._path}' has no design.") 

247 if self._project is None: 247 ↛ 248line 247 didn't jump to line 248 because the condition on line 247 was never true

248 raise Exception(f"Validation: File '{self._path}' has no project.") 

249 

250 def __len__(self) -> int: 

251 """ 

252 Returns number of attributes set on this file. 

253 

254 :returns: The number if attributes set on this file. 

255 """ 

256 return len(self._attributes) 

257 

258 def __getitem__(self, key: Type[Attribute]) -> Any: 

259 """Index access for returning attributes on this file. 

260 

261 :param key: The attribute type. 

262 :returns: The attribute's value. 

263 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

264 """ 

265 if not issubclass(key, Attribute): 265 ↛ 266line 265 didn't jump to line 266 because the condition on line 265 was never true

266 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

267 

268 try: 

269 return self._attributes[key] 

270 except KeyError: 

271 try: 

272 return key.resolve(self, key) 

273 except KeyError: 

274 attribute = key() 

275 self._attributes[key] = attribute 

276 return attribute 

277 

278 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: 

279 """ 

280 Index access for adding or setting attributes on this file. 

281 

282 :param key: The attribute type. 

283 :param value: The attributes value. 

284 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

285 """ 

286 if not issubclass(key, Attribute): 286 ↛ 287line 286 didn't jump to line 287 because the condition on line 286 was never true

287 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

288 

289 self._attributes[key] = value 

290 

291 def __delitem__(self, key: Type[Attribute]) -> None: 

292 """ 

293 Index access for deleting attributes on this file. 

294 

295 :param key: The attribute type. 

296 """ 

297 if not issubclass(key, Attribute): 297 ↛ 298line 297 didn't jump to line 298 because the condition on line 297 was never true

298 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

299 

300 del self._attributes[key] 

301 

302 def __str__(self) -> str: 

303 return f"{self._path}" 

304 

305 

306FileTypes = File 

307 

308 

309@export 

310class HumanReadableContent(metaclass=ExtendedType, mixin=True): 

311 """A file type representing human-readable contents.""" 

312 

313 

314@export 

315class XMLContent(HumanReadableContent, mixin=True): 

316 """A file type representing XML contents.""" 

317 

318 

319@export 

320class YAMLContent(HumanReadableContent, mixin=True): 

321 """A file type representing YAML contents.""" 

322 

323 

324@export 

325class JSONContent(HumanReadableContent, mixin=True): 

326 """A file type representing JSON contents.""" 

327 

328 

329@export 

330class INIContent(HumanReadableContent, mixin=True): 

331 """A file type representing INI contents.""" 

332 

333 

334@export 

335class TOMLContent(HumanReadableContent, mixin=True): 

336 """A file type representing TOML contents.""" 

337 

338 

339@export 

340class TCLContent(HumanReadableContent, mixin=True): 

341 """A file type representing content in TCL code.""" 

342 

343 

344@export 

345class SDCContent(TCLContent, mixin=True): 

346 """A file type representing contents as Synopsys Design Constraints (SDC).""" 

347 

348 

349@export 

350class PythonContent(HumanReadableContent, mixin=True): 

351 """A file type representing contents as Python source code.""" 

352 

353 

354@export 

355class TextFile(File, HumanReadableContent): 

356 """A text file (``*.txt``).""" 

357 

358 

359@export 

360class LogFile(File, HumanReadableContent): 

361 """A log file (``*.log``).""" 

362 

363 

364@export 

365class XMLFile(File, XMLContent): 

366 """An XML file (``*.xml``).""" 

367 

368 

369@export 

370class SourceFile(File): 

371 """Base-class of all source files.""" 

372 

373 

374@export 

375class HDLSourceFile(SourceFile): 

376 """Base-class of all HDL source files.""" 

377 

378 

379@export 

380class RDLSourceFile(SourceFile): 

381 """Base-class of all RDL source files.""" 

382 

383 

384@export 

385class NetlistFile(SourceFile): 

386 """Base-class of all netlist source files.""" 

387 

388 

389@export 

390class EDIFNetlistFile(NetlistFile): 

391 """Netlist file in EDIF (Electronic Design Interchange Format).""" 

392 

393 

394@export 

395class TCLSourceFile(SourceFile, TCLContent): 

396 """A TCL source file.""" 

397 

398 

399@export 

400class VHDLSourceFile(HDLSourceFile, HumanReadableContent): 

401 """ 

402 A VHDL source file (of any language version). 

403 

404 :arg path: Relative or absolute path to the file. 

405 :arg vhdlLibrary: VHDLLibrary this VHDL source file is associated wih. 

406 :arg vhdlVersion: VHDLVersion this VHDL source file is associated wih. 

407 :arg project: Project the file is associated with. 

408 :arg design: Design the file is associated with. 

409 :arg fileSet: Fileset the file is associated with. 

410 """ 

411 

412 _vhdlLibrary: Nullable['VHDLLibrary'] 

413 _vhdlVersion: VHDLVersion 

414 

415 def __init__(self, path: pathlib_Path, vhdlLibrary: Union[str, 'VHDLLibrary'] = None, vhdlVersion: Nullable[VHDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None): 

416 super().__init__(path, project, design, fileSet) 

417 

418 if isinstance(vhdlLibrary, str): 418 ↛ 419line 418 didn't jump to line 419 because the condition on line 418 was never true

419 if design is not None: 

420 try: 

421 vhdlLibrary = design.VHDLLibraries[vhdlLibrary] 

422 except KeyError as ex: 

423 raise Exception(f"VHDL library '{vhdlLibrary}' not found in design '{design.Name}'.") from ex 

424 elif project is not None: 

425 try: 

426 vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary] 

427 except KeyError as ex: 

428 raise Exception(f"VHDL library '{vhdlLibrary}' not found in default design '{project.DefaultDesign.Name}'.") from ex 

429 else: 

430 raise Exception(f"Can't lookup VHDL library because neither 'project' nor 'design' is given as a parameter.") 

431 elif isinstance(vhdlLibrary, VHDLLibrary): 

432 self._vhdlLibrary = vhdlLibrary 

433 vhdlLibrary.AddFile(self) 

434 elif vhdlLibrary is None: 434 ↛ 437line 434 didn't jump to line 437 because the condition on line 434 was always true

435 self._vhdlLibrary = None 

436 else: 

437 ex = TypeError(f"Parameter 'vhdlLibrary' is neither a 'str' nor 'VHDLibrary'.") 

438 if version_info >= (3, 11): # pragma: no cover 

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

440 raise ex 

441 

442 self._vhdlVersion = vhdlVersion 

443 

444 def Validate(self) -> None: 

445 """Validate this VHDL source file.""" 

446 super().Validate() 

447 

448 try: 

449 _ = self.VHDLLibrary 

450 except Exception as ex: 

451 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLLibrary assigned.") from ex 

452 try: 

453 _ = self.VHDLVersion 

454 except Exception as ex: 

455 raise Exception(f"Validation: VHDLSourceFile '{self._path}' (={self.ResolvedPath}) has no VHDLVersion assigned.") from ex 

456 

457 @property 

458 def VHDLLibrary(self) -> 'VHDLLibrary': 

459 """Property setting or returning the VHDL library this VHDL source file is used in.""" 

460 if self._vhdlLibrary is not None: 

461 return self._vhdlLibrary 

462 elif self._fileSet is not None: 

463 return self._fileSet.VHDLLibrary 

464 else: 

465 raise Exception("VHDLLibrary was neither set locally nor globally.") 

466 

467 @VHDLLibrary.setter 

468 def VHDLLibrary(self, value: 'VHDLLibrary') -> None: 

469 self._vhdlLibrary = value 

470 value._files.append(self) 

471 

472 @property 

473 def VHDLVersion(self) -> VHDLVersion: 

474 """Property setting or returning the VHDL version this VHDL source file is used in.""" 

475 if self._vhdlVersion is not None: 

476 return self._vhdlVersion 

477 elif self._fileSet is not None: 

478 return self._fileSet.VHDLVersion 

479 else: 

480 raise Exception("VHDLVersion was neither set locally nor globally.") 

481 

482 @VHDLVersion.setter 

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

484 self._vhdlVersion = value 

485 

486 def __repr__(self) -> str: 

487 return f"<VHDL file: '{self.ResolvedPath}'; lib: '{self.VHDLLibrary}'; version: {self.VHDLVersion}>" 

488 

489 

490class VerilogMixIn(metaclass=ExtendedType, mixin=True): 

491 @property 

492 def VerilogVersion(self) -> SystemVerilogVersion: 

493 """Property setting or returning the Verilog version this Verilog source file is used in.""" 

494 if self._version is not None: 

495 return self._version 

496 elif self._fileSet is not None: 

497 return self._fileSet.VerilogVersion 

498 else: 

499 raise Exception("VerilogVersion was neither set locally nor globally.") 

500 

501 @VerilogVersion.setter 

502 def VerilogVersion(self, value: SystemVerilogVersion) -> None: 

503 self._version = value 

504 

505 

506class SystemVerilogMixIn(metaclass=ExtendedType, mixin=True): 

507 @property 

508 def SVVersion(self) -> SystemVerilogVersion: 

509 """Property setting or returning the SystemVerilog version this SystemVerilog source file is used in.""" 

510 if self._version is not None: 

511 return self._version 

512 elif self._fileSet is not None: 

513 return self._fileSet.SVVersion 

514 else: 

515 raise Exception("SVVersion was neither set locally nor globally.") 

516 

517 @SVVersion.setter 

518 def SVVersion(self, value: SystemVerilogVersion) -> None: 

519 self._version = value 

520 

521 

522@export 

523class VerilogBaseFile(HDLSourceFile, HumanReadableContent): 

524 _version: SystemVerilogVersion 

525 

526 def __init__(self, path: pathlib_Path, version: Nullable[SystemVerilogVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None): 

527 super().__init__(path, project, design, fileSet) 

528 

529 self._version = version 

530 

531 

532@export 

533class VerilogSourceFile(VerilogBaseFile, VerilogMixIn): 

534 """A Verilog source file (of any language version).""" 

535 

536 

537@export 

538class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn): 

539 """A Verilog header file (of any language version).""" 

540 

541 

542@export 

543class SystemVerilogBaseFile(VerilogBaseFile): 

544 ... 

545 

546 

547@export 

548class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn): 

549 """A SystemVerilog source file (of any language version).""" 

550 

551 

552@export 

553class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn): 

554 """A SystemVerilog header file (of any language version).""" 

555 

556 

557@export 

558class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent): 

559 """A SystemRDL source file (of any language version).""" 

560 

561 _srdlVersion: SystemRDLVersion 

562 

563 def __init__(self, path: pathlib_Path, srdlVersion: Nullable[SystemRDLVersion] = None, project: Nullable["Project"] = None, design: Nullable["Design"] = None, fileSet: Nullable["FileSet"] = None): 

564 super().__init__(path, project, design, fileSet) 

565 

566 self._srdlVersion = srdlVersion 

567 

568 @property 

569 def SystemRDLVersion(self) -> SystemRDLVersion: 

570 """Property setting or returning the SystemRDL version this SystemRDL source file is used in.""" 

571 if self._srdlVersion is not None: 

572 return self._srdlVersion 

573 elif self._fileSet is not None: 

574 return self._fileSet.SRDLVersion 

575 else: 

576 raise Exception("SRDLVersion was neither set locally nor globally.") 

577 

578 @SystemRDLVersion.setter 

579 def SystemRDLVersion(self, value: SystemRDLVersion) -> None: 

580 self._srdlVersion = value 

581 

582 

583@export 

584class PythonSourceFile(SourceFile, PythonContent): 

585 """A Python source file.""" 

586 

587 

588# TODO: move to a Cocotb module 

589@export 

590class CocotbPythonFile(PythonSourceFile): 

591 """A Python source file used by Cocotb.""" 

592 

593 

594@export 

595class ConstraintFile(File, HumanReadableContent): 

596 """Base-class of all constraint files.""" 

597 

598 

599@export 

600class ProjectFile(File): 

601 """Base-class of all tool-specific project files.""" 

602 

603 

604@export 

605class CSourceFile(SourceFile): 

606 """Base-class of all ANSI-C source files.""" 

607 

608 

609@export 

610class CppSourceFile(SourceFile): 

611 """Base-class of all ANSI-C++ source files.""" 

612 

613 

614@export 

615class SettingFile(File): 

616 """Base-class of all tool-specific setting files.""" 

617 

618 

619@export 

620class SimulationAnalysisFile(File): 

621 """Base-class of all tool-specific analysis files.""" 

622 

623 

624@export 

625class SimulationElaborationFile(File): 

626 """Base-class of all tool-specific elaboration files.""" 

627 

628 

629@export 

630class SimulationStartFile(File): 

631 """Base-class of all tool-specific simulation start-up files.""" 

632 

633 

634@export 

635class SimulationRunFile(File): 

636 """Base-class of all tool-specific simulation run (execution) files.""" 

637 

638 

639@export 

640class WaveformConfigFile(File): 

641 """Base-class of all tool-specific waveform configuration files.""" 

642 

643 

644@export 

645class WaveformDatabaseFile(File): 

646 """Base-class of all tool-specific waveform database files.""" 

647 

648 

649@export 

650class WaveformExchangeFile(File): 

651 """Base-class of all tool-independent waveform exchange files.""" 

652 

653 

654@export 

655class FileSet(metaclass=ExtendedType, slots=True): 

656 """ 

657 A :term:`FileSet` represents a group of files. Filesets can have sub-filesets. 

658 

659 The order of insertion is preserved. A fileset can be created standalone and 

660 later associated to another fileset, design and/or project. Or a fileset, 

661 design and/or project can be associated immediately while creating the 

662 fileset. 

663 

664 :arg name: Name of this fileset. 

665 :arg topLevel: Name of the fileset's toplevel. 

666 :arg directory: Path of this fileset (absolute or relative to a parent fileset or design). 

667 :arg project: Project the file is associated with. 

668 :arg design: Design the file is associated with. 

669 :arg parent: Parent fileset if this fileset is nested. 

670 :arg vhdlLibrary: Default VHDL library for files in this fileset, if not specified for the file itself. 

671 :arg vhdlVersion: Default VHDL version for files in this fileset, if not specified for the file itself. 

672 :arg verilogVersion: Default Verilog version for files in this fileset, if not specified for the file itself. 

673 :arg svVersion: Default SystemVerilog version for files in this fileset, if not specified for the file itself. 

674 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself. 

675 """ 

676 

677 _name: str 

678 _topLevel: Nullable[str] 

679 _project: Nullable['Project'] 

680 _design: Nullable['Design'] 

681 _directory: pathlib_Path 

682 _parent: Nullable['FileSet'] 

683 _fileSets: Dict[str, 'FileSet'] 

684 _files: List[File] 

685 _set: Set 

686 _attributes: Dict[Type[Attribute], typing_Any] 

687 _vhdlLibraries: Dict[str, 'VHDLLibrary'] 

688 _vhdlLibrary: 'VHDLLibrary' 

689 _vhdlVersion: VHDLVersion 

690 _verilogVersion: SystemVerilogVersion 

691 _svVersion: SystemVerilogVersion 

692 _srdlVersion: SystemRDLVersion 

693 

694 def __init__( 

695 self, 

696 name: str, 

697 topLevel: Nullable[str] = None, 

698 directory: pathlib_Path = pathlib_Path("."), 

699 project: Nullable["Project"] = None, 

700 design: Nullable["Design"] = None, 

701 parent: Nullable['FileSet'] = None, 

702 vhdlLibrary: Union[str, 'VHDLLibrary'] = None, 

703 vhdlVersion: Nullable[VHDLVersion] = None, 

704 verilogVersion: Nullable[SystemVerilogVersion] = None, 

705 svVersion: Nullable[SystemVerilogVersion] = None, 

706 srdlVersion: Nullable[SystemRDLVersion] = None 

707 ): 

708 self._name = name 

709 self._topLevel = topLevel 

710 if project is not None: 

711 self._project = project 

712 self._design = design if design is not None else project.DefaultDesign 

713 

714 elif design is not None: 

715 self._project = design._project 

716 self._design = design 

717 else: 

718 self._project = None 

719 self._design = None 

720 self._directory = directory 

721 self._parent = parent 

722 self._fileSets = {} 

723 self._files = [] 

724 self._set = set() 

725 

726 if design is not None: 

727 design._fileSets[name] = self 

728 

729 self._attributes = {} 

730 self._vhdlLibraries = {} 

731 

732 # TODO: handle if vhdlLibrary is a string 

733 self._vhdlLibrary = vhdlLibrary 

734 self._vhdlVersion = vhdlVersion 

735 self._verilogVersion = verilogVersion 

736 self._svVersion = svVersion 

737 self._srdlVersion = srdlVersion 

738 

739 @property 

740 def Name(self) -> str: 

741 """Property setting or returning the fileset's name.""" 

742 return self._name 

743 

744 @Name.setter 

745 def Name(self, value: str) -> None: 

746 self._name = value 

747 

748 @property 

749 def TopLevel(self) -> str: 

750 """Property setting or returning the fileset's toplevel.""" 

751 return self._topLevel 

752 

753 @TopLevel.setter 

754 def TopLevel(self, value: str) -> None: 

755 self._topLevel = value 

756 

757 @property 

758 def Project(self) -> Nullable['Project']: 

759 """Property setting or returning the project this fileset is used in.""" 

760 return self._project 

761 

762 @Project.setter 

763 def Project(self, value: 'Project') -> None: 

764 self._project = value 

765 

766 @property 

767 def Design(self) -> Nullable['Design']: 

768 """Property setting or returning the design this fileset is used in.""" 

769 if self._design is not None: 

770 return self._design 

771 elif self._parent is not None: 771 ↛ 772line 771 didn't jump to line 772 because the condition on line 771 was never true

772 return self._parent.Design 

773 else: 

774 return None 

775 # TODO: raise exception instead 

776 # QUESTION: how to handle if design and parent is set? 

777 

778 @Design.setter 

779 def Design(self, value: 'Design') -> None: 

780 self._design = value 

781 if self._project is None: 781 ↛ 783line 781 didn't jump to line 783 because the condition on line 781 was always true

782 self._project = value._project 

783 elif self._project is not value._project: 

784 raise Exception("The design's project is not identical to the already assigned project.") 

785 

786 @property 

787 def Directory(self) -> pathlib_Path: 

788 """Property setting or returning the directory this fileset is located in.""" 

789 return self._directory 

790 

791 @Directory.setter 

792 def Directory(self, value: pathlib_Path) -> None: 

793 self._directory = value 

794 

795 @property 

796 def ResolvedPath(self) -> pathlib_Path: 

797 """Read-only property returning the resolved path of this fileset.""" 

798 if self._directory.is_absolute(): 798 ↛ 799line 798 didn't jump to line 799 because the condition on line 798 was never true

799 return self._directory.resolve() 

800 else: 

801 if self._parent is not None: 801 ↛ 802line 801 didn't jump to line 802 because the condition on line 801 was never true

802 directory = self._parent.ResolvedPath 

803 elif self._design is not None: 803 ↛ 805line 803 didn't jump to line 805 because the condition on line 803 was always true

804 directory = self._design.ResolvedPath 

805 elif self._project is not None: 

806 directory = self._project.ResolvedPath 

807 else: 

808 # TODO: message and exception type 

809 raise Exception("") 

810 

811 directory = (directory / self._directory).resolve() 

812 if directory.is_absolute(): 812 ↛ 816line 812 didn't jump to line 816 because the condition on line 812 was always true

813 return directory 

814 else: 

815 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath 

816 return pathlib_Path(path_relpath(directory, pathlib_Path.cwd())) 

817 

818 @property 

819 def Parent(self) -> Nullable['FileSet']: 

820 """Property setting or returning the parent fileset this fileset is used in.""" 

821 return self._parent 

822 

823 @Parent.setter 

824 def Parent(self, value: 'FileSet') -> None: 

825 self._parent = value 

826 value._fileSets[self._name] = self 

827 # TODO: check it it already exists 

828 # QUESTION: make an Add fileset method? 

829 

830 @property 

831 def FileSets(self) -> Dict[str, 'FileSet']: 

832 """Read-only property returning the dictionary of sub-filesets.""" 

833 return self._fileSets 

834 

835 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[bool, str, 'FileSet'] = None) -> Generator[File, None, None]: 

836 """ 

837 Method returning the files of this fileset. 

838 

839 :arg fileType: A filter for file types. Default: ``Any``. 

840 :arg fileSet: Specifies how to handle sub-filesets. 

841 """ 

842 if fileSet is False: 842 ↛ 843line 842 didn't jump to line 843 because the condition on line 842 was never true

843 for file in self._files: 

844 if file.FileType in fileType: 

845 yield file 

846 elif fileSet is None: 846 ↛ 854line 846 didn't jump to line 854 because the condition on line 846 was always true

847 for fileSet in self._fileSets.values(): 847 ↛ 848line 847 didn't jump to line 848 because the loop on line 847 never started

848 for file in fileSet.Files(fileType): 

849 yield file 

850 for file in self._files: 

851 if file.FileType in fileType: 

852 yield file 

853 else: 

854 if isinstance(fileSet, str): 

855 fileSetName = fileSet 

856 try: 

857 fileSet = self._fileSets[fileSetName] 

858 except KeyError as ex: 

859 raise Exception(f"Fileset {fileSetName} not bound to fileset {self.Name}.") from ex 

860 elif not isinstance(fileSet, FileSet): 

861 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.") 

862 

863 for file in fileSet.Files(fileType): 

864 yield file 

865 

866 def AddFileSet(self, fileSet: "FileSet") -> None: 

867 """ 

868 Method to add a single sub-fileset to this fileset. 

869 

870 :arg fileSet: A fileset to add to this fileset as sub-fileset. 

871 """ 

872 if not isinstance(fileSet, FileSet): 872 ↛ 873line 872 didn't jump to line 873 because the condition on line 872 was never true

873 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.") 

874 elif fileSet in self._fileSets: 874 ↛ 875line 874 didn't jump to line 875 because the condition on line 874 was never true

875 raise Exception("Sub-fileset already contains this fileset.") 

876 elif fileSet.Name in self._fileSets.keys(): 876 ↛ 877line 876 didn't jump to line 877 because the condition on line 876 was never true

877 raise Exception(f"Fileset already contains a sub-fileset named '{fileSet.Name}'.") 

878 

879 self._fileSets[fileSet.Name] = fileSet 

880 fileSet._parent = self 

881 

882 def AddFileSets(self, fileSets: Iterable["FileSet"]) -> None: 

883 """ 

884 Method to add a multiple sub-filesets to this fileset. 

885 

886 :arg fileSets: An iterable of filesets to add each to the fileset. 

887 """ 

888 for fileSet in fileSets: 

889 self.AddFileSet(fileSet) 

890 

891 @property 

892 def FileSetCount(self) -> int: 

893 """Returns number of file sets excl. sub-filesets.""" 

894 return len(self._fileSets) 

895 

896 @property 

897 def TotalFileSetCount(self) -> int: 

898 """Returns number of file sets incl. sub-filesets.""" 

899 fileSetCount = len(self._fileSets) 

900 for fileSet in self._fileSets.values(): 

901 fileSetCount += fileSet.TotalFileSetCount 

902 

903 return fileSetCount 

904 

905 def AddFile(self, file: File) -> None: 

906 """ 

907 Method to add a single file to this fileset. 

908 

909 :arg file: A file to add to this fileset. 

910 """ 

911 if not isinstance(file, File): 

912 raise TypeError("Parameter 'file' is not of type ProjectModel.File.") 

913 elif file._fileSet is not None: 

914 ex = ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}'.") 

915 if version_info >= (3, 11): # pragma: no cover 

916 ex.add_note(f"A file can't be assigned to another fileset.") 

917 raise ex 

918 elif file in self._set: 918 ↛ 919line 918 didn't jump to line 919 because the condition on line 918 was never true

919 ex = ValueError(f"File '{file.Path!s}' is already part of this fileset.") 

920 if version_info >= (3, 11): # pragma: no cover 

921 ex.add_note(f"A file can't be added twice to a fileset.") 

922 raise ex 

923 

924 self._files.append(file) 

925 self._set.add(file) 

926 file._fileSet = self 

927 

928 def AddFiles(self, files: Iterable[File]) -> None: 

929 """ 

930 Method to add a multiple files to this fileset. 

931 

932 :arg files: An iterable of files to add each to the fileset. 

933 """ 

934 for file in files: 

935 self.AddFile(file) 

936 

937 @property 

938 def FileCount(self) -> int: 

939 """Returns number of files excl. sub-filesets.""" 

940 return len(self._files) 

941 

942 @property 

943 def TotalFileCount(self) -> int: 

944 """Returns number of files incl. the files in sub-filesets.""" 

945 fileCount = len(self._files) 

946 for fileSet in self._fileSets.values(): 

947 fileCount += fileSet.FileCount 

948 

949 return fileCount 

950 

951 def Validate(self) -> None: 

952 """Validate this fileset.""" 

953 if self._name is None or self._name == "": 953 ↛ 954line 953 didn't jump to line 954 because the condition on line 953 was never true

954 raise Exception("Validation: FileSet has no name.") 

955 

956 if self._directory is None: 956 ↛ 957line 956 didn't jump to line 957 because the condition on line 956 was never true

957 raise Exception(f"Validation: FileSet '{self._name}' has no directory.") 

958 try: 

959 path = self.ResolvedPath 

960 except Exception as ex: 

961 raise Exception(f"Validation: FileSet '{self._name}' could not compute resolved path.") from ex 

962 if not path.exists(): 962 ↛ 963line 962 didn't jump to line 963 because the condition on line 962 was never true

963 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' does not exist.") 

964 if not path.is_dir(): 964 ↛ 965line 964 didn't jump to line 965 because the condition on line 964 was never true

965 raise Exception(f"Validation: FileSet '{self._name}'s directory '{path}' is not a directory.") 

966 

967 if self._design is None: 967 ↛ 968line 967 didn't jump to line 968 because the condition on line 967 was never true

968 raise Exception(f"Validation: FileSet '{self._directory}' has no design.") 

969 if self._project is None: 969 ↛ 970line 969 didn't jump to line 970 because the condition on line 969 was never true

970 raise Exception(f"Validation: FileSet '{self._directory}' has no project.") 

971 

972 for fileSet in self._fileSets.values(): 972 ↛ 973line 972 didn't jump to line 973 because the loop on line 972 never started

973 fileSet.Validate() 

974 for file in self._files: 974 ↛ 975line 974 didn't jump to line 975 because the loop on line 974 never started

975 file.Validate() 

976 

977 def GetOrCreateVHDLLibrary(self, name) -> 'VHDLLibrary': 

978 if name in self._vhdlLibraries: 

979 return self._vhdlLibraries[name] 

980 elif name in self._design._vhdlLibraries: 

981 library = self._design._vhdlLibraries[name] 

982 self._vhdlLibraries[name] = library 

983 return library 

984 else: 

985 library = VHDLLibrary(name, design=self._design, vhdlVersion=self._vhdlVersion) 

986 self._vhdlLibraries[name] = library 

987 return library 

988 

989 @property 

990 def VHDLLibrary(self) -> 'VHDLLibrary': 

991 """Property setting or returning the VHDL library of this fileset.""" 

992 if self._vhdlLibrary is not None: 

993 return self._vhdlLibrary 

994 elif self._parent is not None: 994 ↛ 996line 994 didn't jump to line 996 because the condition on line 994 was always true

995 return self._parent.VHDLLibrary 

996 elif self._design is not None: 

997 return self._design.VHDLLibrary 

998 else: 

999 raise Exception("VHDLLibrary was neither set locally nor globally.") 

1000 

1001 @VHDLLibrary.setter 

1002 def VHDLLibrary(self, value: 'VHDLLibrary') -> None: 

1003 self._vhdlLibrary = value 

1004 

1005 @property 

1006 def VHDLVersion(self) -> VHDLVersion: 

1007 """Property setting or returning the VHDL version of this fileset.""" 

1008 if self._vhdlVersion is not None: 

1009 return self._vhdlVersion 

1010 elif self._parent is not None: 

1011 return self._parent.VHDLVersion 

1012 elif self._design is not None: 1012 ↛ 1015line 1012 didn't jump to line 1015 because the condition on line 1012 was always true

1013 return self._design.VHDLVersion 

1014 else: 

1015 raise Exception("VHDLVersion was neither set locally nor globally.") 

1016 

1017 @VHDLVersion.setter 

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

1019 self._vhdlVersion = value 

1020 

1021 @property 

1022 def VerilogVersion(self) -> SystemVerilogVersion: 

1023 """Property setting or returning the Verilog version of this fileset.""" 

1024 if self._verilogVersion is not None: 

1025 return self._verilogVersion 

1026 elif self._parent is not None: 

1027 return self._parent.VerilogVersion 

1028 elif self._design is not None: 1028 ↛ 1031line 1028 didn't jump to line 1031 because the condition on line 1028 was always true

1029 return self._design.VerilogVersion 

1030 else: 

1031 raise Exception("VerilogVersion was neither set locally nor globally.") 

1032 

1033 @VerilogVersion.setter 

1034 def VerilogVersion(self, value: SystemVerilogVersion) -> None: 

1035 self._verilogVersion = value 

1036 

1037 @property 

1038 def SVVersion(self) -> SystemVerilogVersion: 

1039 """Property setting or returning the SystemVerilog version of this fileset.""" 

1040 if self._svVersion is not None: 

1041 return self._svVersion 

1042 elif self._parent is not None: 

1043 return self._parent.SVVersion 

1044 elif self._design is not None: 1044 ↛ 1047line 1044 didn't jump to line 1047 because the condition on line 1044 was always true

1045 return self._design.SVVersion 

1046 else: 

1047 raise Exception("SVVersion was neither set locally nor globally.") 

1048 

1049 @SVVersion.setter 

1050 def SVVersion(self, value: SystemVerilogVersion) -> None: 

1051 self._svVersion = value 

1052 

1053 @property 

1054 def SRDLVersion(self) -> SystemRDLVersion: 

1055 if self._srdlVersion is not None: 

1056 return self._srdlVersion 

1057 elif self._parent is not None: 

1058 return self._parent.SRDLVersion 

1059 elif self._design is not None: 

1060 return self._design.SRDLVersion 

1061 else: 

1062 raise Exception("SRDLVersion was neither set locally nor globally.") 

1063 

1064 @SRDLVersion.setter 

1065 def SRDLVersion(self, value: SystemRDLVersion) -> None: 

1066 self._srdlVersion = value 

1067 

1068 def __len__(self) -> int: 

1069 """ 

1070 Returns number of attributes set on this fileset. 

1071 

1072 :returns: The number if attributes set on this fileset. 

1073 """ 

1074 return len(self._attributes) 

1075 

1076 def __getitem__(self, key: Type[Attribute]) -> Any: 

1077 """Index access for returning attributes on this fileset. 

1078 

1079 :param key: The attribute type. 

1080 :returns: The attribute's value. 

1081 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1082 """ 

1083 if not issubclass(key, Attribute): 1083 ↛ 1084line 1083 didn't jump to line 1084 because the condition on line 1083 was never true

1084 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1085 

1086 try: 

1087 return self._attributes[key] 

1088 except KeyError: 

1089 return key.resolve(self, key) 

1090 

1091 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: 

1092 """ 

1093 Index access for adding or setting attributes on this fileset. 

1094 

1095 :param key: The attribute type. 

1096 :param value: The attributes value. 

1097 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1098 """ 

1099 if not issubclass(key, Attribute): 1099 ↛ 1100line 1099 didn't jump to line 1100 because the condition on line 1099 was never true

1100 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1101 

1102 self._attributes[key] = value 

1103 

1104 def __delitem__(self, key: Type[Attribute]) -> None: 

1105 """ 

1106 Index access for deleting attributes on this fileset. 

1107 

1108 :param key: The attribute type. 

1109 """ 

1110 if not issubclass(key, Attribute): 1110 ↛ 1111line 1110 didn't jump to line 1111 because the condition on line 1110 was never true

1111 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1112 

1113 del self._attributes[key] 

1114 

1115 def __str__(self) -> str: 

1116 """Returns the fileset's name.""" 

1117 return self._name 

1118 

1119 

1120@export 

1121class VHDLLibrary(metaclass=ExtendedType, slots=True): 

1122 """ 

1123 A :term:`VHDLLibrary` represents a group of VHDL source files compiled into the same VHDL library. 

1124 

1125 :arg name: The VHDL libraries' name. 

1126 :arg project: Project the VHDL library is associated with. 

1127 :arg design: Design the VHDL library is associated with. 

1128 :arg vhdlVersion: Default VHDL version for files in this VHDL library, if not specified for the file itself. 

1129 """ 

1130 

1131 _name: str 

1132 _project: Nullable['Project'] 

1133 _design: Nullable['Design'] 

1134 _files: List[File] 

1135 _vhdlVersion: VHDLVersion 

1136 

1137 _dependencyNode: Vertex 

1138 

1139 def __init__( 

1140 self, 

1141 name: str, 

1142 project: Nullable["Project"] = None, 

1143 design: Nullable["Design"] = None, 

1144 vhdlVersion: Nullable[VHDLVersion] = None 

1145 ): 

1146 self._name = name 

1147 if project is not None: 

1148 self._project = project 

1149 self._design = project._defaultDesign if design is None else design 

1150 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph) 

1151 

1152 if name in self._design._vhdlLibraries: 1152 ↛ 1153line 1152 didn't jump to line 1153 because the condition on line 1152 was never true

1153 raise Exception(f"Library '{name}' already in design '{self._design.Name}'.") 

1154 else: 

1155 self._design._vhdlLibraries[name] = self 

1156 

1157 elif design is not None: 

1158 self._project = design._project 

1159 self._design = design 

1160 self._dependencyNode = Vertex(value=self, graph=design._vhdlLibraryDependencyGraph) 

1161 

1162 if name in design._vhdlLibraries: 1162 ↛ 1163line 1162 didn't jump to line 1163 because the condition on line 1162 was never true

1163 raise Exception(f"Library '{name}' already in design '{design.Name}'.") 

1164 else: 

1165 design._vhdlLibraries[name] = self 

1166 

1167 else: 

1168 self._project = None 

1169 self._design = None 

1170 self._dependencyNode = None 

1171 

1172 self._files = [] 

1173 self._vhdlVersion = vhdlVersion 

1174 

1175 @property 

1176 def Name(self) -> str: 

1177 return self._name 

1178 

1179 @property 

1180 def Project(self) -> Nullable['Project']: 

1181 """Property setting or returning the project this VHDL library is used in.""" 

1182 return self._project 

1183 

1184 @Project.setter 

1185 def Project(self, value: 'Project') -> None: 

1186 if not isinstance(value, Project): 1186 ↛ 1187line 1186 didn't jump to line 1187 because the condition on line 1186 was never true

1187 raise TypeError("Parameter 'value' is not of type 'Project'.") 

1188 

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

1190 # TODO: unlink VHDLLibrary from project 

1191 self._project = None 

1192 else: 

1193 self._project = value 

1194 if self._design is None: 1194 ↛ exitline 1194 didn't return from function 'Project' because the condition on line 1194 was always true

1195 self._design = value._defaultDesign 

1196 

1197 @property 

1198 def Design(self) -> Nullable['Design']: 

1199 """Property setting or returning the design this VHDL library is used in.""" 

1200 return self._design 

1201 

1202 @Design.setter 

1203 def Design(self, value: 'Design') -> None: 

1204 if not isinstance(value, Design): 

1205 raise TypeError("Parameter 'value' is not of type 'Design'.") 

1206 

1207 if value is None: 

1208 # TODO: unlink VHDLLibrary from design 

1209 self._design = None 

1210 else: 

1211 if self._design is None: 

1212 self._design = value 

1213 self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph) 

1214 elif self._design is not value: 

1215 # TODO: move VHDLLibrary to other design 

1216 # TODO: create new vertex in dependency graph and remove vertex from old graph 

1217 self._design = value 

1218 else: 

1219 pass 

1220 

1221 if self._project is None: 

1222 self._project = value._project 

1223 elif self._project is not value._project: 

1224 raise Exception("The design's project is not identical to the already assigned project.") 

1225 

1226 @property 

1227 def Files(self) -> Generator[File, None, None]: 

1228 """Read-only property to return all files in this VHDL library.""" 

1229 for file in self._files: 

1230 yield file 

1231 

1232 @property 

1233 def VHDLVersion(self) -> VHDLVersion: 

1234 """Property setting or returning the VHDL version of this VHDL library.""" 

1235 if self._vhdlVersion is not None: 

1236 return self._vhdlVersion 

1237 elif self._design is not None: 1237 ↛ 1240line 1237 didn't jump to line 1240 because the condition on line 1237 was always true

1238 return self._design.VHDLVersion 

1239 else: 

1240 raise Exception("VHDLVersion is not set on VHDLLibrary nor parent object.") 

1241 

1242 @VHDLVersion.setter 

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

1244 self._vhdlVersion = value 

1245 

1246 def AddDependency(self, library: 'VHDLLibrary') -> None: 

1247 library.parent = self 

1248 

1249 def AddFile(self, vhdlFile: VHDLSourceFile) -> None: 

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

1251 ex = TypeError(f"Parameter 'vhdlFile' is not a 'VHDLSourceFile'.") 

1252 if version_info >= (3, 11): # pragma: no cover 

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

1254 raise ex 

1255 

1256 self._files.append(vhdlFile) 

1257 

1258 def AddFiles(self, vhdlFiles: Iterable[VHDLSourceFile]) -> None: 

1259 for vhdlFile in vhdlFiles: 

1260 if not isinstance(vhdlFile, VHDLSourceFile): 

1261 raise TypeError(f"Item '{vhdlFile}' in parameter 'vhdlFiles' is not a 'VHDLSourceFile'.") 

1262 

1263 self._files.append(vhdlFile) 

1264 

1265 @property 

1266 def FileCount(self) -> int: 

1267 """Returns number of files.""" 

1268 return len(self._files) 

1269 

1270 def __len__(self) -> int: 

1271 """ 

1272 Returns number of attributes set on this VHDL library. 

1273 

1274 :returns: The number if attributes set on this VHDL library. 

1275 """ 

1276 return len(self._attributes) 

1277 

1278 def __getitem__(self, key: Type[Attribute]) -> Any: 

1279 """Index access for returning attributes on this VHDL library. 

1280 

1281 :param key: The attribute type. 

1282 :returns: The attribute's value. 

1283 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1284 """ 

1285 if not issubclass(key, Attribute): 

1286 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1287 

1288 try: 

1289 return self._attributes[key] 

1290 except KeyError: 

1291 return key.resolve(self, key) 

1292 

1293 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: 

1294 """ 

1295 Index access for adding or setting attributes on this VHDL library. 

1296 

1297 :param key: The attribute type. 

1298 :param value: The attributes value. 

1299 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1300 """ 

1301 if not issubclass(key, Attribute): 

1302 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1303 

1304 self._attributes[key] = value 

1305 

1306 def __delitem__(self, key: Type[Attribute]) -> None: 

1307 """ 

1308 Index access for deleting attributes on this VHDL library. 

1309 

1310 :param key: The attribute type. 

1311 """ 

1312 if not issubclass(key, Attribute): 

1313 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1314 

1315 del self._attributes[key] 

1316 

1317 def __str__(self) -> str: 

1318 """Returns the VHDL library's name.""" 

1319 return self._name 

1320 

1321 

1322@export 

1323class Design(metaclass=ExtendedType, slots=True): 

1324 """ 

1325 A :term:`Design` represents a group of filesets and the source files therein. 

1326 

1327 Each design contains at least one fileset - the :term:`default fileset`. For 

1328 designs with VHDL source files, a independent `VHDLLibraries` overlay structure 

1329 exists. 

1330 

1331 :arg name: The design's name. 

1332 :arg topLevel: Name of the design's toplevel. 

1333 :arg directory: Path of this design (absolute or relative to the project). 

1334 :arg project: Project the design is associated with. 

1335 :arg vhdlVersion: Default VHDL version for files in this design, if not specified for the file itself. 

1336 :arg verilogVersion: Default Verilog version for files in this design, if not specified for the file itself. 

1337 :arg svVersion: Default SystemVerilog version for files in this design, if not specified for the file itself. 

1338 :arg srdlVersion: Default SystemRDL version for files in this fileset, if not specified for the file itself. 

1339 """ 

1340 

1341 _name: str 

1342 _topLevel: Nullable[str] 

1343 _project: Nullable['Project'] 

1344 _directory: pathlib_Path 

1345 _fileSets: Dict[str, FileSet] 

1346 _defaultFileSet: Nullable[FileSet] 

1347 _attributes: Dict[Type[Attribute], typing_Any] 

1348 

1349 _vhdlLibraries: Dict[str, VHDLLibrary] 

1350 _vhdlVersion: VHDLVersion 

1351 _verilogVersion: SystemVerilogVersion 

1352 _svVersion: SystemVerilogVersion 

1353 _srdlVersion: SystemRDLVersion 

1354 _externalVHDLLibraries: List 

1355 

1356 _vhdlLibraryDependencyGraph: Graph 

1357 _fileDependencyGraph: Graph 

1358 

1359 def __init__( 

1360 self, 

1361 name: str, 

1362 topLevel: Nullable[str] = None, 

1363 directory: pathlib_Path = pathlib_Path("."), 

1364 project: Nullable["Project"] = None, 

1365 vhdlVersion: Nullable[VHDLVersion] = None, 

1366 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1367 svVersion: Nullable[SystemVerilogVersion] = None, 

1368 srdlVersion: Nullable[SystemRDLVersion] = None 

1369 ): 

1370 self._name = name 

1371 self._topLevel = topLevel 

1372 self._project = project 

1373 if project is not None: 

1374 project._designs[name] = self 

1375 self._directory = directory 

1376 self._fileSets = {} 

1377 self._defaultFileSet = FileSet("default", project=project, design=self) 

1378 self._attributes = {} 

1379 self._vhdlLibraries = {} 

1380 self._vhdlVersion = vhdlVersion 

1381 self._verilogVersion = verilogVersion 

1382 self._svVersion = svVersion 

1383 self._srdlVersion = srdlVersion 

1384 self._externalVHDLLibraries = [] 

1385 

1386 self._vhdlLibraryDependencyGraph = Graph() 

1387 self._fileDependencyGraph = Graph() 

1388 

1389 @property 

1390 def Name(self) -> str: 

1391 """Property setting or returning the design's name.""" 

1392 return self._name 

1393 

1394 @Name.setter 

1395 def Name(self, value: str) -> None: 

1396 self._name = value 

1397 

1398 @property 

1399 def TopLevel(self) -> str: 

1400 """Property setting or returning the fileset's toplevel.""" 

1401 return self._topLevel 

1402 

1403 @TopLevel.setter 

1404 def TopLevel(self, value: str) -> None: 

1405 self._topLevel = value 

1406 

1407 @property 

1408 def Project(self) -> Nullable['Project']: 

1409 """Property setting or returning the project this design is used in.""" 

1410 return self._project 

1411 

1412 @Project.setter 

1413 def Project(self, value: 'Project') -> None: 

1414 self._project = value 

1415 

1416 @property 

1417 def Directory(self) -> pathlib_Path: 

1418 """Property setting or returning the directory this design is located in.""" 

1419 return self._directory 

1420 

1421 @Directory.setter 

1422 def Directory(self, value: pathlib_Path) -> None: 

1423 self._directory = value 

1424 

1425 @property 

1426 def ResolvedPath(self) -> pathlib_Path: 

1427 """Read-only property returning the resolved path of this fileset.""" 

1428 if self._directory.is_absolute(): 1428 ↛ 1429line 1428 didn't jump to line 1429 because the condition on line 1428 was never true

1429 return self._directory.resolve() 

1430 elif self._project is not None: 1430 ↛ 1440line 1430 didn't jump to line 1440 because the condition on line 1430 was always true

1431 path = (self._project.ResolvedPath / self._directory).resolve() 

1432 

1433 if path.is_absolute(): 1433 ↛ 1437line 1433 didn't jump to line 1437 because the condition on line 1433 was always true

1434 return path 

1435 else: 

1436 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath 

1437 return pathlib_Path(path_relpath(path, pathlib_Path.cwd())) 

1438 else: 

1439 # TODO: message and exception type 

1440 raise Exception("") 

1441 

1442 @property 

1443 def DefaultFileSet(self) -> FileSet: 

1444 """Property setting or returning the default fileset of this design.""" 

1445 return self._defaultFileSet 

1446 

1447 @DefaultFileSet.setter 

1448 def DefaultFileSet(self, value: Union[str, FileSet]) -> None: 

1449 if isinstance(value, str): 

1450 if value not in self._fileSets.keys(): 

1451 raise Exception(f"Fileset '{value}' is not in this design.") 

1452 

1453 self._defaultFileSet = self._fileSets[value] 

1454 elif isinstance(value, FileSet): 

1455 if value not in self.FileSets: 

1456 raise Exception(f"Fileset '{value}' is not associated to this design.") 

1457 

1458 self._defaultFileSet = value 

1459 else: 

1460 raise ValueError("Unsupported parameter type for 'value'.") 

1461 

1462 # TODO: return generator with another method 

1463 @property 

1464 def FileSets(self) -> Dict[str, FileSet]: 

1465 """Read-only property returning the dictionary of filesets.""" 

1466 return self._fileSets 

1467 

1468 def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[str, FileSet] = None) -> Generator[File, None, None]: 

1469 """ 

1470 Method returning the files of this design. 

1471 

1472 :arg fileType: A filter for file types. Default: ``Any``. 

1473 :arg fileSet: Specifies if all files from all filesets (``fileSet=None``) are files from a single fileset are returned. 

1474 """ 

1475 if fileSet is None: 

1476 for fileSet in self._fileSets.values(): 

1477 for file in fileSet.Files(fileType): 

1478 yield file 

1479 else: 

1480 if isinstance(fileSet, str): 1480 ↛ 1485line 1480 didn't jump to line 1485 because the condition on line 1480 was always true

1481 try: 

1482 fileSet = self._fileSets[fileSet] 

1483 except KeyError as ex: 

1484 raise Exception(f"Fileset {fileSet.Name} not bound to design {self.Name}.") from ex 

1485 elif not isinstance(fileSet, FileSet): 

1486 raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.") 

1487 

1488 for file in fileSet.Files(fileType): 

1489 yield file 

1490 

1491 def Validate(self) -> None: 

1492 """Validate this design.""" 

1493 if self._name is None or self._name == "": 1493 ↛ 1494line 1493 didn't jump to line 1494 because the condition on line 1493 was never true

1494 raise Exception("Validation: Design has no name.") 

1495 

1496 if self._directory is None: 1496 ↛ 1497line 1496 didn't jump to line 1497 because the condition on line 1496 was never true

1497 raise Exception(f"Validation: Design '{self._name}' has no directory.") 

1498 try: 

1499 path = self.ResolvedPath 

1500 except Exception as ex: 

1501 raise Exception(f"Validation: Design '{self._name}' could not compute resolved path.") from ex 

1502 if not path.exists(): 1502 ↛ 1503line 1502 didn't jump to line 1503 because the condition on line 1502 was never true

1503 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' does not exist.") 

1504 if not path.is_dir(): 1504 ↛ 1505line 1504 didn't jump to line 1505 because the condition on line 1504 was never true

1505 raise Exception(f"Validation: Design '{self._name}'s directory '{path}' is not a directory.") 

1506 

1507 if len(self._fileSets) == 0: 1507 ↛ 1508line 1507 didn't jump to line 1508 because the condition on line 1507 was never true

1508 raise Exception(f"Validation: Design '{self._name}' has no fileset.") 

1509 try: 

1510 if self._defaultFileSet is not self._fileSets[self._defaultFileSet.Name]: 1510 ↛ 1511line 1510 didn't jump to line 1511 because the condition on line 1510 was never true

1511 raise Exception(f"Validation: Design '{self._name}'s default fileset is the same as listed in filesets.") 

1512 except KeyError as ex: 

1513 raise Exception(f"Validation: Design '{self._name}'s default fileset is not in list of filesets.") from ex 

1514 if self._project is None: 1514 ↛ 1515line 1514 didn't jump to line 1515 because the condition on line 1514 was never true

1515 raise Exception(f"Validation: Design '{self._path}' has no project.") 

1516 

1517 for fileSet in self._fileSets.values(): 

1518 fileSet.Validate() 

1519 

1520 @property 

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

1522 return self._vhdlLibraries 

1523 

1524 @property 

1525 def VHDLVersion(self) -> VHDLVersion: 

1526 if self._vhdlVersion is not None: 

1527 return self._vhdlVersion 

1528 elif self._project is not None: 1528 ↛ 1531line 1528 didn't jump to line 1531 because the condition on line 1528 was always true

1529 return self._project.VHDLVersion 

1530 else: 

1531 raise Exception("VHDLVersion was neither set locally nor globally.") 

1532 

1533 @VHDLVersion.setter 

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

1535 self._vhdlVersion = value 

1536 

1537 @property 

1538 def VerilogVersion(self) -> SystemVerilogVersion: 

1539 if self._verilogVersion is not None: 

1540 return self._verilogVersion 

1541 elif self._project is not None: 1541 ↛ 1544line 1541 didn't jump to line 1544 because the condition on line 1541 was always true

1542 return self._project.VerilogVersion 

1543 else: 

1544 raise Exception("VerilogVersion was neither set locally nor globally.") 

1545 

1546 @VerilogVersion.setter 

1547 def VerilogVersion(self, value: SystemVerilogVersion) -> None: 

1548 self._verilogVersion = value 

1549 

1550 @property 

1551 def SVVersion(self) -> SystemVerilogVersion: 

1552 if self._svVersion is not None: 

1553 return self._svVersion 

1554 elif self._project is not None: 1554 ↛ 1557line 1554 didn't jump to line 1557 because the condition on line 1554 was always true

1555 return self._project.SVVersion 

1556 else: 

1557 raise Exception("SVVersion was neither set locally nor globally.") 

1558 

1559 @SVVersion.setter 

1560 def SVVersion(self, value: SystemVerilogVersion) -> None: 

1561 self._svVersion = value 

1562 

1563 @property 

1564 def SRDLVersion(self) -> SystemRDLVersion: 

1565 if self._srdlVersion is not None: 

1566 return self._srdlVersion 

1567 elif self._project is not None: 

1568 return self._project.SRDLVersion 

1569 else: 

1570 raise Exception("SRDLVersion was neither set locally nor globally.") 

1571 

1572 @SRDLVersion.setter 

1573 def SRDLVersion(self, value: SystemRDLVersion) -> None: 

1574 self._srdlVersion = value 

1575 

1576 @property 

1577 def ExternalVHDLLibraries(self) -> List: 

1578 return self._externalVHDLLibraries 

1579 

1580 def AddFileSet(self, fileSet: FileSet) -> None: 

1581 if not isinstance(fileSet, FileSet): 

1582 raise ValueError("Parameter 'fileSet' is not of type ProjectModel.FileSet.") 

1583 elif fileSet in self._fileSets: 

1584 raise Exception("Design already contains this fileset.") 

1585 elif fileSet.Name in self._fileSets.keys(): 

1586 raise Exception(f"Design already contains a fileset named '{fileSet.Name}'.") 

1587 

1588 self._fileSets[fileSet.Name] = fileSet 

1589 fileSet.Design = self 

1590 fileSet._parent = self 

1591 

1592 def AddFileSets(self, fileSets: Iterable[FileSet]) -> None: 

1593 for fileSet in fileSets: 

1594 self.AddFileSet(fileSet) 

1595 

1596 @property 

1597 def FileSetCount(self) -> int: 

1598 """Returns number of file sets excl. sub-filesets.""" 

1599 return len(self._fileSets) 

1600 

1601 @property 

1602 def TotalFileSetCount(self) -> int: 

1603 """Returns number of file sets incl. sub-filesets.""" 

1604 fileSetCount = len(self._fileSets) 

1605 for fileSet in self._fileSets.values(): 

1606 fileSetCount += fileSet.TotalFileSetCount 

1607 

1608 return fileSetCount 

1609 

1610 def AddFile(self, file: File) -> None: 

1611 if file.FileSet is None: 1611 ↛ 1614line 1611 didn't jump to line 1614 because the condition on line 1611 was always true

1612 self._defaultFileSet.AddFile(file) 

1613 else: 

1614 raise ValueError(f"File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}' and can't be assigned via Design to a default fileset.") 

1615 

1616 def AddFiles(self, files: Iterable[File]) -> None: 

1617 for file in files: 

1618 self.AddFile(file) 

1619 

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

1621 if vhdlLibrary.Name in self._vhdlLibraries: 

1622 if self._vhdlLibraries[vhdlLibrary.Name] is vhdlLibrary: 

1623 raise Exception(f"The VHDLLibrary '{vhdlLibrary.Name}' was already added to the design.") 

1624 else: 

1625 raise Exception(f"A VHDLLibrary with same name ('{vhdlLibrary.Name}') already exists for this design.") 

1626 

1627 

1628 def __len__(self) -> int: 

1629 """ 

1630 Returns number of attributes set on this design. 

1631 

1632 :returns: The number if attributes set on this design. 

1633 """ 

1634 return len(self._attributes) 

1635 

1636 def __getitem__(self, key: Type[Attribute]) -> Any: 

1637 """Index access for returning attributes on this design. 

1638 

1639 :param key: The attribute type. 

1640 :returns: The attribute's value. 

1641 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1642 """ 

1643 if not issubclass(key, Attribute): 1643 ↛ 1644line 1643 didn't jump to line 1644 because the condition on line 1643 was never true

1644 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1645 

1646 try: 

1647 return self._attributes[key] 

1648 except KeyError: 

1649 return key.resolve(self, key) 

1650 

1651 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: 

1652 """ 

1653 Index access for adding or setting attributes on this design. 

1654 

1655 :param key: The attribute type. 

1656 :param value: The attributes value. 

1657 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1658 """ 

1659 if not issubclass(key, Attribute): 1659 ↛ 1660line 1659 didn't jump to line 1660 because the condition on line 1659 was never true

1660 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1661 

1662 self._attributes[key] = value 

1663 

1664 def __delitem__(self, key: Type[Attribute]) -> None: 

1665 """ 

1666 Index access for deleting attributes on this design. 

1667 

1668 :param key: The attribute type. 

1669 """ 

1670 if not issubclass(key, Attribute): 1670 ↛ 1671line 1670 didn't jump to line 1671 because the condition on line 1670 was never true

1671 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1672 

1673 del self._attributes[key] 

1674 

1675 def __str__(self) -> str: 

1676 return self._name 

1677 

1678 

1679@export 

1680class Project(metaclass=ExtendedType, slots=True): 

1681 """ 

1682 A :term:`Project` represents a group of designs and the source files therein. 

1683 

1684 :arg name: The project's name. 

1685 :arg rootDirectory: Base-path to the project. 

1686 :arg vhdlVersion: Default VHDL version for files in this project, if not specified for the file itself. 

1687 :arg verilogVersion: Default Verilog version for files in this project, if not specified for the file itself. 

1688 :arg svVersion: Default SystemVerilog version for files in this project, if not specified for the file itself. 

1689 """ 

1690 

1691 _name: str 

1692 _rootDirectory: pathlib_Path 

1693 _designs: Dict[str, Design] 

1694 _defaultDesign: Design 

1695 _attributes: Dict[Type[Attribute], typing_Any] 

1696 

1697 _vhdlVersion: VHDLVersion 

1698 _verilogVersion: SystemVerilogVersion 

1699 _svVersion: SystemVerilogVersion 

1700 _srdlVersion: SystemRDLVersion 

1701 

1702 def __init__( 

1703 self, 

1704 name: str, 

1705 rootDirectory: pathlib_Path = pathlib_Path("."), 

1706 vhdlVersion: Nullable[VHDLVersion] = None, 

1707 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1708 svVersion: Nullable[SystemVerilogVersion] = None 

1709 ): 

1710 self._name = name 

1711 self._rootDirectory = rootDirectory 

1712 self._designs = {} 

1713 self._defaultDesign = Design("default", project=self) 

1714 self._attributes = {} 

1715 self._vhdlVersion = vhdlVersion 

1716 self._verilogVersion = verilogVersion 

1717 self._svVersion = svVersion 

1718 

1719 @property 

1720 def Name(self) -> str: 

1721 """Property setting or returning the project's name.""" 

1722 return self._name 

1723 

1724 @property 

1725 def RootDirectory(self) -> pathlib_Path: 

1726 """Property setting or returning the root directory this project is located in.""" 

1727 return self._rootDirectory 

1728 

1729 @RootDirectory.setter 

1730 def RootDirectory(self, value: pathlib_Path) -> None: 

1731 self._rootDirectory = value 

1732 

1733 @property 

1734 def ResolvedPath(self) -> pathlib_Path: 

1735 """Read-only property returning the resolved path of this fileset.""" 

1736 path = self._rootDirectory.resolve() 

1737 if self._rootDirectory.is_absolute(): 

1738 return path 

1739 else: 

1740 # WORKAROUND: https://stackoverflow.com/questions/67452690/pathlib-path-relative-to-vs-os-path-relpath 

1741 return pathlib_Path(path_relpath(path, pathlib_Path.cwd())) 

1742 

1743 # TODO: return generator with another method 

1744 @property 

1745 def Designs(self) -> Dict[str, Design]: 

1746 return self._designs 

1747 

1748 @property 

1749 def DefaultDesign(self) -> Design: 

1750 return self._defaultDesign 

1751 

1752 def Validate(self) -> None: 

1753 """Validate this project.""" 

1754 if self._name is None or self._name == "": 1754 ↛ 1755line 1754 didn't jump to line 1755 because the condition on line 1754 was never true

1755 raise Exception("Validation: Project has no name.") 

1756 

1757 if self._rootDirectory is None: 1757 ↛ 1758line 1757 didn't jump to line 1758 because the condition on line 1757 was never true

1758 raise Exception(f"Validation: Project '{self._name}' has no root directory.") 

1759 try: 

1760 path = self.ResolvedPath 

1761 except Exception as ex: 

1762 raise Exception(f"Validation: Project '{self._name}' could not compute resolved path.") from ex 

1763 if not path.exists(): 1763 ↛ 1764line 1763 didn't jump to line 1764 because the condition on line 1763 was never true

1764 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' does not exist.") 

1765 if not path.is_dir(): 1765 ↛ 1766line 1765 didn't jump to line 1766 because the condition on line 1765 was never true

1766 raise Exception(f"Validation: Project '{self._name}'s directory '{path}' is not a directory.") 

1767 

1768 if len(self._designs) == 0: 1768 ↛ 1769line 1768 didn't jump to line 1769 because the condition on line 1768 was never true

1769 raise Exception(f"Validation: Project '{self._name}' has no design.") 

1770 try: 

1771 if self._defaultDesign is not self._designs[self._defaultDesign.Name]: 1771 ↛ 1772line 1771 didn't jump to line 1772 because the condition on line 1771 was never true

1772 raise Exception(f"Validation: Project '{self._name}'s default design is the same as listed in designs.") 

1773 except KeyError as ex: 

1774 raise Exception(f"Validation: Project '{self._name}'s default design is not in list of designs.") from ex 

1775 

1776 for design in self._designs.values(): 

1777 design.Validate() 

1778 

1779 @property 

1780 def DesignCount(self) -> int: 

1781 """Returns number of designs.""" 

1782 return len(self._designs) 

1783 

1784 @property 

1785 def VHDLVersion(self) -> VHDLVersion: 

1786 # TODO: check for None and return exception 

1787 return self._vhdlVersion 

1788 

1789 @VHDLVersion.setter 

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

1791 self._vhdlVersion = value 

1792 

1793 @property 

1794 def VerilogVersion(self) -> SystemVerilogVersion: 

1795 # TODO: check for None and return exception 

1796 return self._verilogVersion 

1797 

1798 @VerilogVersion.setter 

1799 def VerilogVersion(self, value: SystemVerilogVersion) -> None: 

1800 self._verilogVersion = value 

1801 

1802 @property 

1803 def SVVersion(self) -> SystemVerilogVersion: 

1804 # TODO: check for None and return exception 

1805 return self._svVersion 

1806 

1807 @SVVersion.setter 

1808 def SVVersion(self, value: SystemVerilogVersion) -> None: 

1809 self._svVersion = value 

1810 

1811 @property 

1812 def SRDLVersion(self) -> SystemRDLVersion: 

1813 # TODO: check for None and return exception 

1814 return self._srdlVersion 

1815 

1816 @SRDLVersion.setter 

1817 def SRDLVersion(self, value: SystemRDLVersion) -> None: 

1818 self._srdlVersion = value 

1819 

1820 def __len__(self) -> int: 

1821 """ 

1822 Returns number of attributes set on this project. 

1823 

1824 :returns: The number if attributes set on this project. 

1825 """ 

1826 return len(self._attributes) 

1827 

1828 def __getitem__(self, key: Type[Attribute]) -> Any: 

1829 """Index access for returning attributes on this project. 

1830 

1831 :param key: The attribute type. 

1832 :returns: The attribute's value. 

1833 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1834 """ 

1835 if not issubclass(key, Attribute): 1835 ↛ 1836line 1835 didn't jump to line 1836 because the condition on line 1835 was never true

1836 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1837 

1838 try: 

1839 return self._attributes[key] 

1840 except KeyError: 

1841 return key.resolve(self, key) 

1842 

1843 def __setitem__(self, key: Type[Attribute], value: typing_Any) -> None: 

1844 """ 

1845 Index access for adding or setting attributes on this project. 

1846 

1847 :param key: The attribute type. 

1848 :param value: The attributes value. 

1849 :raises TypeError: When parameter 'key' is not a subclass of Attribute. 

1850 """ 

1851 if not issubclass(key, Attribute): 1851 ↛ 1852line 1851 didn't jump to line 1852 because the condition on line 1851 was never true

1852 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1853 

1854 self._attributes[key] = value 

1855 

1856 def __delitem__(self, key: Type[Attribute]) -> None: 

1857 """ 

1858 Index access for deleting attributes on this project. 

1859 

1860 :param key: The attribute type. 

1861 """ 

1862 if not issubclass(key, Attribute): 1862 ↛ 1863line 1862 didn't jump to line 1863 because the condition on line 1862 was never true

1863 raise TypeError("Parameter 'key' is not an 'Attribute'.") 

1864 

1865 del self._attributes[key] 

1866 

1867 def __str__(self) -> str: 

1868 return self._name