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

1070 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-18 17:08 +0000

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

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

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

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

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

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

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

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

14# Copyright 2017-2025 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-2025, Patrick Lehmann, Unai Martinez-Corral" 

36__license__ = "Apache License, Version 2.0" 

37__version__ = "0.6.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): 

62 return obj._fileSet[key] 

63 elif isinstance(obj, FileSet): 

64 return obj._design[key] 

65 elif isinstance(obj, Design): 65 ↛ 68line 65 didn't jump to line 68 because the condition on line 65 was always true

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

170 Read-only property to access the path to the file. 

171 

172 :returns: The file's path. 

173 """ 

174 return self._path 

175 

176 # TODO: setter? 

177 

178 @property 

179 def ResolvedPath(self) -> pathlib_Path: 

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

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

182 return self._path.resolve() 

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

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

185 

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

187 return path 

188 else: 

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

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

191 else: 

192 # TODO: message and exception type 

193 raise Exception("") 

194 

195 @property 

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

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

198 return self._project 

199 

200 @Project.setter 

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

202 self._project = value 

203 

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

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

206 

207 @property 

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

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

210 return self._design 

211 

212 @Design.setter 

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

214 self._design = value 

215 

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

217 self._design.DefaultFileSet.AddFile(self) 

218 

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

220 self._project = value._project 

221 elif self._project is not value._project: 

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

223 

224 @property 

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

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

227 return self._fileSet 

228 

229 @FileSet.setter 

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

231 self._fileSet = value 

232 value._files.append(self) 

233 

234 def Validate(self) -> None: 

235 """Validate this file.""" 

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

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

238 try: 

239 path = self.ResolvedPath 

240 except Exception as ex: 

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

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

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

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

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

246 

247 if self._fileSet 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 fileset.") 

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

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

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

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

253 

254 def __len__(self) -> int: 

255 """ 

256 Returns number of attributes set on this file. 

257 

258 :returns: The number of attributes set on this file. 

259 """ 

260 return len(self._attributes) 

261 

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

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

264 

265 :param key: The attribute type. 

266 :returns: The attribute's value. 

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

268 """ 

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

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

271 

272 try: 

273 return self._attributes[key] 

274 except KeyError: 

275 try: 

276 return key.resolve(self, key) 

277 except KeyError: 

278 attribute = key() 

279 self._attributes[key] = attribute 

280 return attribute 

281 

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

283 """ 

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

285 

286 :param key: The attribute type. 

287 :param value: The attributes value. 

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

289 """ 

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

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

292 

293 self._attributes[key] = value 

294 

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

296 """ 

297 Index access for deleting attributes on this file. 

298 

299 :param key: The attribute type. 

300 """ 

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

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

303 

304 del self._attributes[key] 

305 

306 def __str__(self) -> str: 

307 return f"{self._path}" 

308 

309 

310FileTypes = File 

311 

312 

313@export 

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

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

316 

317 

318@export 

319class XMLContent(HumanReadableContent, mixin=True): 

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

321 

322 

323@export 

324class YAMLContent(HumanReadableContent, mixin=True): 

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

326 

327 

328@export 

329class JSONContent(HumanReadableContent, mixin=True): 

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

331 

332 

333@export 

334class INIContent(HumanReadableContent, mixin=True): 

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

336 

337 

338@export 

339class TOMLContent(HumanReadableContent, mixin=True): 

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

341 

342 

343@export 

344class TCLContent(HumanReadableContent, mixin=True): 

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

346 

347 

348@export 

349class SDCContent(TCLContent, mixin=True): 

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

351 

352 

353@export 

354class PythonContent(HumanReadableContent, mixin=True): 

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

356 

357 

358@export 

359class TextFile(File, HumanReadableContent): 

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

361 

362 

363@export 

364class LogFile(File, HumanReadableContent): 

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

366 

367 

368@export 

369class XMLFile(File, XMLContent): 

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

371 

372 

373@export 

374class SourceFile(File): 

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

376 

377 

378@export 

379class HDLSourceFile(SourceFile): 

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

381 

382 

383@export 

384class RDLSourceFile(SourceFile): 

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

386 

387 

388@export 

389class NetlistFile(SourceFile): 

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

391 

392 

393@export 

394class EDIFNetlistFile(NetlistFile): 

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

396 

397 

398@export 

399class TCLSourceFile(SourceFile, TCLContent): 

400 """A TCL source file.""" 

401 

402 

403@export 

404class VHDLSourceFile(HDLSourceFile, HumanReadableContent): 

405 """ 

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

407 

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

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

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

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

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

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

414 """ 

415 

416 _vhdlLibrary: Nullable['VHDLLibrary'] 

417 _vhdlVersion: VHDLVersion 

418 

419 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): 

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

421 

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

423 if design is not None: 

424 try: 

425 vhdlLibrary = design.VHDLLibraries[vhdlLibrary] 

426 except KeyError as ex: 

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

428 elif project is not None: 

429 try: 

430 vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary] 

431 except KeyError as ex: 

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

433 else: 

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

435 elif isinstance(vhdlLibrary, VHDLLibrary): 

436 self._vhdlLibrary = vhdlLibrary 

437 vhdlLibrary.AddFile(self) 

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

439 self._vhdlLibrary = None 

440 else: 

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

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

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

444 raise ex 

445 

446 self._vhdlVersion = vhdlVersion 

447 

448 def Validate(self) -> None: 

449 """Validate this VHDL source file.""" 

450 super().Validate() 

451 

452 try: 

453 _ = self.VHDLLibrary 

454 except Exception as ex: 

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

456 try: 

457 _ = self.VHDLVersion 

458 except Exception as ex: 

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

460 

461 @property 

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

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

464 if self._vhdlLibrary is not None: 

465 return self._vhdlLibrary 

466 elif self._fileSet is not None: 

467 return self._fileSet.VHDLLibrary 

468 else: 

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

470 

471 @VHDLLibrary.setter 

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

473 self._vhdlLibrary = value 

474 value._files.append(self) 

475 

476 @property 

477 def VHDLVersion(self) -> VHDLVersion: 

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

479 if self._vhdlVersion is not None: 

480 return self._vhdlVersion 

481 elif self._fileSet is not None: 

482 return self._fileSet.VHDLVersion 

483 else: 

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

485 

486 @VHDLVersion.setter 

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

488 self._vhdlVersion = value 

489 

490 def __repr__(self) -> str: 

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

492 

493 

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

495 @property 

496 def VerilogVersion(self) -> SystemVerilogVersion: 

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

498 if self._version is not None: 

499 return self._version 

500 elif self._fileSet is not None: 

501 return self._fileSet.VerilogVersion 

502 else: 

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

504 

505 @VerilogVersion.setter 

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

507 self._version = value 

508 

509 

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

511 @property 

512 def SVVersion(self) -> SystemVerilogVersion: 

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

514 if self._version is not None: 

515 return self._version 

516 elif self._fileSet is not None: 

517 return self._fileSet.SVVersion 

518 else: 

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

520 

521 @SVVersion.setter 

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

523 self._version = value 

524 

525 

526@export 

527class VerilogBaseFile(HDLSourceFile, HumanReadableContent): 

528 _version: SystemVerilogVersion 

529 

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

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

532 

533 self._version = version 

534 

535 

536@export 

537class VerilogSourceFile(VerilogBaseFile, VerilogMixIn): 

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

539 

540 

541@export 

542class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn): 

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

544 

545 

546@export 

547class SystemVerilogBaseFile(VerilogBaseFile): 

548 ... 

549 

550 

551@export 

552class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn): 

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

554 

555 

556@export 

557class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn): 

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

559 

560 

561@export 

562class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent): 

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

564 

565 _srdlVersion: SystemRDLVersion 

566 

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

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

569 

570 self._srdlVersion = srdlVersion 

571 

572 @property 

573 def SystemRDLVersion(self) -> SystemRDLVersion: 

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

575 if self._srdlVersion is not None: 

576 return self._srdlVersion 

577 elif self._fileSet is not None: 

578 return self._fileSet.SRDLVersion 

579 else: 

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

581 

582 @SystemRDLVersion.setter 

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

584 self._srdlVersion = value 

585 

586 

587@export 

588class PythonSourceFile(SourceFile, PythonContent): 

589 """A Python source file.""" 

590 

591 

592# TODO: move to a Cocotb module 

593@export 

594class CocotbPythonFile(PythonSourceFile): 

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

596 

597 

598@export 

599class ConstraintFile(File, HumanReadableContent): 

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

601 

602 

603@export 

604class ProjectFile(File): 

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

606 

607 

608@export 

609class CSourceFile(SourceFile): 

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

611 

612 

613@export 

614class CppSourceFile(SourceFile): 

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

616 

617 

618@export 

619class SettingFile(File): 

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

621 

622 

623@export 

624class SimulationAnalysisFile(File): 

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

626 

627 

628@export 

629class SimulationElaborationFile(File): 

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

631 

632 

633@export 

634class SimulationStartFile(File): 

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

636 

637 

638@export 

639class SimulationRunFile(File): 

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

641 

642 

643@export 

644class WaveformConfigFile(File): 

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

646 

647 

648@export 

649class WaveformDatabaseFile(File): 

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

651 

652 

653@export 

654class WaveformExchangeFile(File): 

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

656 

657 

658@export 

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

660 """ 

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

662 

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

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

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

666 fileset. 

667 

668 :arg name: Name of this fileset. 

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

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

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

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

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

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

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

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

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

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

679 """ 

680 

681 _name: str 

682 _topLevel: Nullable[str] 

683 _project: Nullable['Project'] 

684 _design: Nullable['Design'] 

685 _directory: pathlib_Path 

686 _parent: Nullable['FileSet'] 

687 _fileSets: Dict[str, 'FileSet'] 

688 _files: List[File] 

689 _set: Set 

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

691 _vhdlLibraries: Dict[str, 'VHDLLibrary'] 

692 _vhdlLibrary: 'VHDLLibrary' 

693 _vhdlVersion: VHDLVersion 

694 _verilogVersion: SystemVerilogVersion 

695 _svVersion: SystemVerilogVersion 

696 _srdlVersion: SystemRDLVersion 

697 

698 def __init__( 

699 self, 

700 name: str, 

701 topLevel: Nullable[str] = None, 

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

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

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

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

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

707 vhdlVersion: Nullable[VHDLVersion] = None, 

708 verilogVersion: Nullable[SystemVerilogVersion] = None, 

709 svVersion: Nullable[SystemVerilogVersion] = None, 

710 srdlVersion: Nullable[SystemRDLVersion] = None 

711 ): 

712 self._name = name 

713 self._topLevel = topLevel 

714 if project is not None: 

715 self._project = project 

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

717 

718 elif design is not None: 

719 self._project = design._project 

720 self._design = design 

721 else: 

722 self._project = None 

723 self._design = None 

724 self._directory = directory 

725 self._parent = parent 

726 self._fileSets = {} 

727 self._files = [] 

728 self._set = set() 

729 

730 if design is not None: 

731 design._fileSets[name] = self 

732 

733 self._attributes = {} 

734 self._vhdlLibraries = {} 

735 

736 # TODO: handle if vhdlLibrary is a string 

737 self._vhdlLibrary = vhdlLibrary 

738 self._vhdlVersion = vhdlVersion 

739 self._verilogVersion = verilogVersion 

740 self._svVersion = svVersion 

741 self._srdlVersion = srdlVersion 

742 

743 @property 

744 def Name(self) -> str: 

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

746 return self._name 

747 

748 @Name.setter 

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

750 self._name = value 

751 

752 @property 

753 def TopLevel(self) -> str: 

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

755 return self._topLevel 

756 

757 @TopLevel.setter 

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

759 self._topLevel = value 

760 

761 @property 

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

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

764 return self._project 

765 

766 @Project.setter 

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

768 self._project = value 

769 

770 @property 

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

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

773 if self._design is not None: 

774 return self._design 

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

776 return self._parent.Design 

777 else: 

778 return None 

779 # TODO: raise exception instead 

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

781 

782 @Design.setter 

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

784 self._design = value 

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

786 self._project = value._project 

787 elif self._project is not value._project: 

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

789 

790 @property 

791 def Directory(self) -> pathlib_Path: 

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

793 return self._directory 

794 

795 @Directory.setter 

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

797 self._directory = value 

798 

799 @property 

800 def ResolvedPath(self) -> pathlib_Path: 

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

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

803 return self._directory.resolve() 

804 else: 

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

806 directory = self._parent.ResolvedPath 

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

808 directory = self._design.ResolvedPath 

809 elif self._project is not None: 

810 directory = self._project.ResolvedPath 

811 else: 

812 # TODO: message and exception type 

813 raise Exception("") 

814 

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

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

817 return directory 

818 else: 

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

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

821 

822 @property 

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

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

825 return self._parent 

826 

827 @Parent.setter 

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

829 self._parent = value 

830 value._fileSets[self._name] = self 

831 # TODO: check it it already exists 

832 # QUESTION: make an Add fileset method? 

833 

834 @property 

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

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

837 return self._fileSets 

838 

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

840 """ 

841 Method returning the files of this fileset. 

842 

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

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

845 """ 

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

847 for file in self._files: 

848 if file.FileType in fileType: 

849 yield file 

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

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

852 for file in fileSet.Files(fileType): 

853 yield file 

854 for file in self._files: 

855 if file.FileType in fileType: 

856 yield file 

857 else: 

858 if isinstance(fileSet, str): 

859 fileSetName = fileSet 

860 try: 

861 fileSet = self._fileSets[fileSetName] 

862 except KeyError as ex: 

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

864 elif not isinstance(fileSet, FileSet): 

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

866 

867 for file in fileSet.Files(fileType): 

868 yield file 

869 

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

871 """ 

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

873 

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

875 """ 

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

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

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

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

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

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

882 

883 self._fileSets[fileSet.Name] = fileSet 

884 fileSet._parent = self 

885 

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

887 """ 

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

889 

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

891 """ 

892 for fileSet in fileSets: 

893 self.AddFileSet(fileSet) 

894 

895 @property 

896 def FileSetCount(self) -> int: 

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

898 return len(self._fileSets) 

899 

900 @property 

901 def TotalFileSetCount(self) -> int: 

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

903 fileSetCount = len(self._fileSets) 

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

905 fileSetCount += fileSet.TotalFileSetCount 

906 

907 return fileSetCount 

908 

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

910 """ 

911 Method to add a single file to this fileset. 

912 

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

914 """ 

915 if not isinstance(file, File): 

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

917 elif file._fileSet is not None: 

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

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

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

921 raise ex 

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

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

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

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

926 raise ex 

927 

928 self._files.append(file) 

929 self._set.add(file) 

930 file._fileSet = self 

931 

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

933 """ 

934 Method to add a multiple files to this fileset. 

935 

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

937 """ 

938 for file in files: 

939 self.AddFile(file) 

940 

941 @property 

942 def FileCount(self) -> int: 

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

944 return len(self._files) 

945 

946 @property 

947 def TotalFileCount(self) -> int: 

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

949 fileCount = len(self._files) 

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

951 fileCount += fileSet.FileCount 

952 

953 return fileCount 

954 

955 def Validate(self) -> None: 

956 """Validate this fileset.""" 

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

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

959 

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

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

962 try: 

963 path = self.ResolvedPath 

964 except Exception as ex: 

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

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

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

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

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

970 

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

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

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

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

975 

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

977 fileSet.Validate() 

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

979 file.Validate() 

980 

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

982 if name in self._vhdlLibraries: 

983 return self._vhdlLibraries[name] 

984 elif name in self._design._vhdlLibraries: 

985 library = self._design._vhdlLibraries[name] 

986 self._vhdlLibraries[name] = library 

987 return library 

988 else: 

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

990 self._vhdlLibraries[name] = library 

991 return library 

992 

993 @property 

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

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

996 if self._vhdlLibrary is not None: 

997 return self._vhdlLibrary 

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

999 return self._parent.VHDLLibrary 

1000 elif self._design is not None: 

1001 return self._design.VHDLLibrary 

1002 else: 

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

1004 

1005 @VHDLLibrary.setter 

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

1007 self._vhdlLibrary = value 

1008 

1009 @property 

1010 def VHDLVersion(self) -> VHDLVersion: 

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

1012 if self._vhdlVersion is not None: 

1013 return self._vhdlVersion 

1014 elif self._parent is not None: 

1015 return self._parent.VHDLVersion 

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

1017 return self._design.VHDLVersion 

1018 else: 

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

1020 

1021 @VHDLVersion.setter 

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

1023 self._vhdlVersion = value 

1024 

1025 @property 

1026 def VerilogVersion(self) -> SystemVerilogVersion: 

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

1028 if self._verilogVersion is not None: 

1029 return self._verilogVersion 

1030 elif self._parent is not None: 

1031 return self._parent.VerilogVersion 

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

1033 return self._design.VerilogVersion 

1034 else: 

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

1036 

1037 @VerilogVersion.setter 

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

1039 self._verilogVersion = value 

1040 

1041 @property 

1042 def SVVersion(self) -> SystemVerilogVersion: 

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

1044 if self._svVersion is not None: 

1045 return self._svVersion 

1046 elif self._parent is not None: 

1047 return self._parent.SVVersion 

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

1049 return self._design.SVVersion 

1050 else: 

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

1052 

1053 @SVVersion.setter 

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

1055 self._svVersion = value 

1056 

1057 @property 

1058 def SRDLVersion(self) -> SystemRDLVersion: 

1059 if self._srdlVersion is not None: 

1060 return self._srdlVersion 

1061 elif self._parent is not None: 

1062 return self._parent.SRDLVersion 

1063 elif self._design is not None: 

1064 return self._design.SRDLVersion 

1065 else: 

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

1067 

1068 @SRDLVersion.setter 

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

1070 self._srdlVersion = value 

1071 

1072 def __len__(self) -> int: 

1073 """ 

1074 Returns number of attributes set on this fileset. 

1075 

1076 :returns: The number of attributes set on this fileset. 

1077 """ 

1078 return len(self._attributes) 

1079 

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

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

1082 

1083 :param key: The attribute type. 

1084 :returns: The attribute's value. 

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

1086 """ 

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

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

1089 

1090 try: 

1091 return self._attributes[key] 

1092 except KeyError: 

1093 return key.resolve(self, key) 

1094 

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

1096 """ 

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

1098 

1099 :param key: The attribute type. 

1100 :param value: The attributes value. 

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

1102 """ 

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

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

1105 

1106 self._attributes[key] = value 

1107 

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

1109 """ 

1110 Index access for deleting attributes on this fileset. 

1111 

1112 :param key: The attribute type. 

1113 """ 

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

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

1116 

1117 del self._attributes[key] 

1118 

1119 def __str__(self) -> str: 

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

1121 return self._name 

1122 

1123 

1124@export 

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

1126 """ 

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

1128 

1129 :arg name: The VHDL libraries' name. 

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

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

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

1133 """ 

1134 

1135 _name: str 

1136 _project: Nullable['Project'] 

1137 _design: Nullable['Design'] 

1138 _files: List[File] 

1139 _vhdlVersion: VHDLVersion 

1140 

1141 _dependencyNode: Vertex 

1142 

1143 def __init__( 

1144 self, 

1145 name: str, 

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

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

1148 vhdlVersion: Nullable[VHDLVersion] = None 

1149 ): 

1150 self._name = name 

1151 if project is not None: 

1152 self._project = project 

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

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

1155 

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

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

1158 else: 

1159 self._design._vhdlLibraries[name] = self 

1160 

1161 elif design is not None: 

1162 self._project = design._project 

1163 self._design = design 

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

1165 

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

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

1168 else: 

1169 design._vhdlLibraries[name] = self 

1170 

1171 else: 

1172 self._project = None 

1173 self._design = None 

1174 self._dependencyNode = None 

1175 

1176 self._files = [] 

1177 self._vhdlVersion = vhdlVersion 

1178 

1179 @property 

1180 def Name(self) -> str: 

1181 return self._name 

1182 

1183 @property 

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

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

1186 return self._project 

1187 

1188 @Project.setter 

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

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

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

1192 

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

1194 # TODO: unlink VHDLLibrary from project 

1195 self._project = None 

1196 else: 

1197 self._project = value 

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

1199 self._design = value._defaultDesign 

1200 

1201 @property 

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

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

1204 return self._design 

1205 

1206 @Design.setter 

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

1208 if not isinstance(value, Design): 

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

1210 

1211 if value is None: 

1212 # TODO: unlink VHDLLibrary from design 

1213 self._design = None 

1214 else: 

1215 if self._design is None: 

1216 self._design = value 

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

1218 elif self._design is not value: 

1219 # TODO: move VHDLLibrary to other design 

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

1221 self._design = value 

1222 else: 

1223 pass 

1224 

1225 if self._project is None: 

1226 self._project = value._project 

1227 elif self._project is not value._project: 

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

1229 

1230 @property 

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

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

1233 for file in self._files: 

1234 yield file 

1235 

1236 @property 

1237 def VHDLVersion(self) -> VHDLVersion: 

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

1239 if self._vhdlVersion is not None: 

1240 return self._vhdlVersion 

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

1242 return self._design.VHDLVersion 

1243 else: 

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

1245 

1246 @VHDLVersion.setter 

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

1248 self._vhdlVersion = value 

1249 

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

1251 library.parent = self 

1252 

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

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

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

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

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

1258 raise ex 

1259 

1260 self._files.append(vhdlFile) 

1261 

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

1263 for vhdlFile in vhdlFiles: 

1264 if not isinstance(vhdlFile, VHDLSourceFile): 

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

1266 

1267 self._files.append(vhdlFile) 

1268 

1269 @property 

1270 def FileCount(self) -> int: 

1271 """Returns number of files.""" 

1272 return len(self._files) 

1273 

1274 def __len__(self) -> int: 

1275 """ 

1276 Returns number of attributes set on this VHDL library. 

1277 

1278 :returns: The number of attributes set on this VHDL library. 

1279 """ 

1280 return len(self._attributes) 

1281 

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

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

1284 

1285 :param key: The attribute type. 

1286 :returns: The attribute's value. 

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

1288 """ 

1289 if not issubclass(key, Attribute): 

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

1291 

1292 try: 

1293 return self._attributes[key] 

1294 except KeyError: 

1295 return key.resolve(self, key) 

1296 

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

1298 """ 

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

1300 

1301 :param key: The attribute type. 

1302 :param value: The attributes value. 

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

1304 """ 

1305 if not issubclass(key, Attribute): 

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

1307 

1308 self._attributes[key] = value 

1309 

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

1311 """ 

1312 Index access for deleting attributes on this VHDL library. 

1313 

1314 :param key: The attribute type. 

1315 """ 

1316 if not issubclass(key, Attribute): 

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

1318 

1319 del self._attributes[key] 

1320 

1321 def __str__(self) -> str: 

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

1323 return self._name 

1324 

1325 

1326@export 

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

1328 """ 

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

1330 

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

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

1333 exists. 

1334 

1335 :arg name: The design's name. 

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

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

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

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

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

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

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

1343 """ 

1344 

1345 _name: str 

1346 _topLevel: Nullable[str] 

1347 _project: Nullable['Project'] 

1348 _directory: pathlib_Path 

1349 _fileSets: Dict[str, FileSet] 

1350 _defaultFileSet: Nullable[FileSet] 

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

1352 

1353 _vhdlLibraries: Dict[str, VHDLLibrary] 

1354 _vhdlVersion: VHDLVersion 

1355 _verilogVersion: SystemVerilogVersion 

1356 _svVersion: SystemVerilogVersion 

1357 _srdlVersion: SystemRDLVersion 

1358 _externalVHDLLibraries: List 

1359 

1360 _vhdlLibraryDependencyGraph: Graph 

1361 _fileDependencyGraph: Graph 

1362 

1363 def __init__( 

1364 self, 

1365 name: str, 

1366 topLevel: Nullable[str] = None, 

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

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

1369 vhdlVersion: Nullable[VHDLVersion] = None, 

1370 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1371 svVersion: Nullable[SystemVerilogVersion] = None, 

1372 srdlVersion: Nullable[SystemRDLVersion] = None 

1373 ): 

1374 self._name = name 

1375 self._topLevel = topLevel 

1376 self._project = project 

1377 if project is not None: 

1378 project._designs[name] = self 

1379 self._directory = directory 

1380 self._fileSets = {} 

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

1382 self._attributes = {} 

1383 self._vhdlLibraries = {} 

1384 self._vhdlVersion = vhdlVersion 

1385 self._verilogVersion = verilogVersion 

1386 self._svVersion = svVersion 

1387 self._srdlVersion = srdlVersion 

1388 self._externalVHDLLibraries = [] 

1389 

1390 self._vhdlLibraryDependencyGraph = Graph() 

1391 self._fileDependencyGraph = Graph() 

1392 

1393 @property 

1394 def Name(self) -> str: 

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

1396 return self._name 

1397 

1398 @Name.setter 

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

1400 self._name = value 

1401 

1402 @property 

1403 def TopLevel(self) -> str: 

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

1405 return self._topLevel 

1406 

1407 @TopLevel.setter 

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

1409 self._topLevel = value 

1410 

1411 @property 

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

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

1414 return self._project 

1415 

1416 @Project.setter 

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

1418 self._project = value 

1419 

1420 @property 

1421 def Directory(self) -> pathlib_Path: 

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

1423 return self._directory 

1424 

1425 @Directory.setter 

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

1427 self._directory = value 

1428 

1429 @property 

1430 def ResolvedPath(self) -> pathlib_Path: 

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

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

1433 return self._directory.resolve() 

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

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

1436 

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

1438 return path 

1439 else: 

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

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

1442 else: 

1443 # TODO: message and exception type 

1444 raise Exception("") 

1445 

1446 @property 

1447 def DefaultFileSet(self) -> FileSet: 

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

1449 return self._defaultFileSet 

1450 

1451 @DefaultFileSet.setter 

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

1453 if isinstance(value, str): 

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

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

1456 

1457 self._defaultFileSet = self._fileSets[value] 

1458 elif isinstance(value, FileSet): 

1459 if value not in self.FileSets: 

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

1461 

1462 self._defaultFileSet = value 

1463 else: 

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

1465 

1466 # TODO: return generator with another method 

1467 @property 

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

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

1470 return self._fileSets 

1471 

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

1473 """ 

1474 Method returning the files of this design. 

1475 

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

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

1478 """ 

1479 if fileSet is None: 

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

1481 for file in fileSet.Files(fileType): 

1482 yield file 

1483 else: 

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

1485 try: 

1486 fileSet = self._fileSets[fileSet] 

1487 except KeyError as ex: 

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

1489 elif not isinstance(fileSet, FileSet): 

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

1491 

1492 for file in fileSet.Files(fileType): 

1493 yield file 

1494 

1495 def Validate(self) -> None: 

1496 """Validate this design.""" 

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

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

1499 

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

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

1502 try: 

1503 path = self.ResolvedPath 

1504 except Exception as ex: 

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

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

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

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

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

1510 

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

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

1513 try: 

1514 if self._defaultFileSet is not self._fileSets[self._defaultFileSet.Name]: 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._name}'s default fileset is the same as listed in filesets.") 

1516 except KeyError as ex: 

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

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

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

1520 

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

1522 fileSet.Validate() 

1523 

1524 @property 

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

1526 return self._vhdlLibraries 

1527 

1528 @property 

1529 def VHDLVersion(self) -> VHDLVersion: 

1530 if self._vhdlVersion is not None: 

1531 return self._vhdlVersion 

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

1533 return self._project.VHDLVersion 

1534 else: 

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

1536 

1537 @VHDLVersion.setter 

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

1539 self._vhdlVersion = value 

1540 

1541 @property 

1542 def VerilogVersion(self) -> SystemVerilogVersion: 

1543 if self._verilogVersion is not None: 

1544 return self._verilogVersion 

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

1546 return self._project.VerilogVersion 

1547 else: 

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

1549 

1550 @VerilogVersion.setter 

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

1552 self._verilogVersion = value 

1553 

1554 @property 

1555 def SVVersion(self) -> SystemVerilogVersion: 

1556 if self._svVersion is not None: 

1557 return self._svVersion 

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

1559 return self._project.SVVersion 

1560 else: 

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

1562 

1563 @SVVersion.setter 

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

1565 self._svVersion = value 

1566 

1567 @property 

1568 def SRDLVersion(self) -> SystemRDLVersion: 

1569 if self._srdlVersion is not None: 

1570 return self._srdlVersion 

1571 elif self._project is not None: 

1572 return self._project.SRDLVersion 

1573 else: 

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

1575 

1576 @SRDLVersion.setter 

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

1578 self._srdlVersion = value 

1579 

1580 @property 

1581 def ExternalVHDLLibraries(self) -> List: 

1582 return self._externalVHDLLibraries 

1583 

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

1585 if not isinstance(fileSet, FileSet): 

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

1587 elif fileSet in self._fileSets: 

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

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

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

1591 

1592 self._fileSets[fileSet.Name] = fileSet 

1593 fileSet.Design = self 

1594 fileSet._parent = self 

1595 

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

1597 for fileSet in fileSets: 

1598 self.AddFileSet(fileSet) 

1599 

1600 @property 

1601 def FileSetCount(self) -> int: 

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

1603 return len(self._fileSets) 

1604 

1605 @property 

1606 def TotalFileSetCount(self) -> int: 

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

1608 fileSetCount = len(self._fileSets) 

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

1610 fileSetCount += fileSet.TotalFileSetCount 

1611 

1612 return fileSetCount 

1613 

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

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

1616 self._defaultFileSet.AddFile(file) 

1617 else: 

1618 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.") 

1619 

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

1621 for file in files: 

1622 self.AddFile(file) 

1623 

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

1625 if vhdlLibrary.Name in self._vhdlLibraries: 

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

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

1628 else: 

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

1630 

1631 

1632 def __len__(self) -> int: 

1633 """ 

1634 Returns number of attributes set on this design. 

1635 

1636 :returns: The number of attributes set on this design. 

1637 """ 

1638 return len(self._attributes) 

1639 

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

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

1642 

1643 :param key: The attribute type. 

1644 :returns: The attribute's value. 

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

1646 """ 

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

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

1649 

1650 try: 

1651 return self._attributes[key] 

1652 except KeyError: 

1653 return key.resolve(self, key) 

1654 

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

1656 """ 

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

1658 

1659 :param key: The attribute type. 

1660 :param value: The attributes value. 

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

1662 """ 

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

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

1665 

1666 self._attributes[key] = value 

1667 

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

1669 """ 

1670 Index access for deleting attributes on this design. 

1671 

1672 :param key: The attribute type. 

1673 """ 

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

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

1676 

1677 del self._attributes[key] 

1678 

1679 def __str__(self) -> str: 

1680 return self._name 

1681 

1682 

1683@export 

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

1685 """ 

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

1687 

1688 :arg name: The project's name. 

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

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

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

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

1693 """ 

1694 

1695 _name: str 

1696 _rootDirectory: pathlib_Path 

1697 _designs: Dict[str, Design] 

1698 _defaultDesign: Design 

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

1700 

1701 _vhdlVersion: VHDLVersion 

1702 _verilogVersion: SystemVerilogVersion 

1703 _svVersion: SystemVerilogVersion 

1704 _srdlVersion: SystemRDLVersion 

1705 

1706 def __init__( 

1707 self, 

1708 name: str, 

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

1710 vhdlVersion: Nullable[VHDLVersion] = None, 

1711 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1712 svVersion: Nullable[SystemVerilogVersion] = None 

1713 ): 

1714 self._name = name 

1715 self._rootDirectory = rootDirectory 

1716 self._designs = {} 

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

1718 self._attributes = {} 

1719 self._vhdlVersion = vhdlVersion 

1720 self._verilogVersion = verilogVersion 

1721 self._svVersion = svVersion 

1722 

1723 @property 

1724 def Name(self) -> str: 

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

1726 return self._name 

1727 

1728 @property 

1729 def RootDirectory(self) -> pathlib_Path: 

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

1731 return self._rootDirectory 

1732 

1733 @RootDirectory.setter 

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

1735 self._rootDirectory = value 

1736 

1737 @property 

1738 def ResolvedPath(self) -> pathlib_Path: 

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

1740 path = self._rootDirectory.resolve() 

1741 if self._rootDirectory.is_absolute(): 

1742 return path 

1743 else: 

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

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

1746 

1747 # TODO: return generator with another method 

1748 @property 

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

1750 return self._designs 

1751 

1752 @property 

1753 def DefaultDesign(self) -> Design: 

1754 return self._defaultDesign 

1755 

1756 def Validate(self) -> None: 

1757 """Validate this project.""" 

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

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

1760 

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

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

1763 try: 

1764 path = self.ResolvedPath 

1765 except Exception as ex: 

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

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

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

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

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

1771 

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

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

1774 try: 

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

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

1777 except KeyError as ex: 

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

1779 

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

1781 design.Validate() 

1782 

1783 @property 

1784 def DesignCount(self) -> int: 

1785 """Returns number of designs.""" 

1786 return len(self._designs) 

1787 

1788 @property 

1789 def VHDLVersion(self) -> VHDLVersion: 

1790 # TODO: check for None and return exception 

1791 return self._vhdlVersion 

1792 

1793 @VHDLVersion.setter 

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

1795 self._vhdlVersion = value 

1796 

1797 @property 

1798 def VerilogVersion(self) -> SystemVerilogVersion: 

1799 # TODO: check for None and return exception 

1800 return self._verilogVersion 

1801 

1802 @VerilogVersion.setter 

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

1804 self._verilogVersion = value 

1805 

1806 @property 

1807 def SVVersion(self) -> SystemVerilogVersion: 

1808 # TODO: check for None and return exception 

1809 return self._svVersion 

1810 

1811 @SVVersion.setter 

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

1813 self._svVersion = value 

1814 

1815 @property 

1816 def SRDLVersion(self) -> SystemRDLVersion: 

1817 # TODO: check for None and return exception 

1818 return self._srdlVersion 

1819 

1820 @SRDLVersion.setter 

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

1822 self._srdlVersion = value 

1823 

1824 def __len__(self) -> int: 

1825 """ 

1826 Returns number of attributes set on this project. 

1827 

1828 :returns: The number of attributes set on this project. 

1829 """ 

1830 return len(self._attributes) 

1831 

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

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

1834 

1835 :param key: The attribute type. 

1836 :returns: The attribute's value. 

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

1838 """ 

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

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

1841 

1842 try: 

1843 return self._attributes[key] 

1844 except KeyError: 

1845 return key.resolve(self, key) 

1846 

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

1848 """ 

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

1850 

1851 :param key: The attribute type. 

1852 :param value: The attributes value. 

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

1854 """ 

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

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

1857 

1858 self._attributes[key] = value 

1859 

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

1861 """ 

1862 Index access for deleting attributes on this project. 

1863 

1864 :param key: The attribute type. 

1865 """ 

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

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

1868 

1869 del self._attributes[key] 

1870 

1871 def __str__(self) -> str: 

1872 return self._name