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

1072 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-23 22:26 +0000

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

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

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

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

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

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

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

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

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

36__license__ = "Apache License, Version 2.0" 

37__version__ = "0.6.2" 

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, Self 

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

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

85 cls.Any = cls 

86 

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

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

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]) -> typing_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) -> 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) -> 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) -> 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 ) -> None: 

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 # FIXME: no library property. add a DefaultVHDLLibrary property 

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]) -> typing_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 _attributes: Dict[Attribute, typing_Any] 

1142 _dependencyNode: Vertex 

1143 

1144 def __init__( 

1145 self, 

1146 name: str, 

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

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

1149 vhdlVersion: Nullable[VHDLVersion] = None 

1150 ) -> None: 

1151 self._name = name 

1152 self._attributes = {} 

1153 

1154 if project is not None: 

1155 self._project = project 

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

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

1158 

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

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

1161 else: 

1162 self._design._vhdlLibraries[name] = self 

1163 

1164 elif design is not None: 

1165 self._project = design._project 

1166 self._design = design 

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

1168 

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

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

1171 else: 

1172 design._vhdlLibraries[name] = self 

1173 

1174 else: 

1175 self._project = None 

1176 self._design = None 

1177 self._dependencyNode = None 

1178 

1179 self._files = [] 

1180 self._vhdlVersion = vhdlVersion 

1181 

1182 @property 

1183 def Name(self) -> str: 

1184 return self._name 

1185 

1186 @property 

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

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

1189 return self._project 

1190 

1191 @Project.setter 

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

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

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

1195 

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

1197 # TODO: unlink VHDLLibrary from project 

1198 self._project = None 

1199 else: 

1200 self._project = value 

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

1202 self._design = value._defaultDesign 

1203 

1204 @property 

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

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

1207 return self._design 

1208 

1209 @Design.setter 

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

1211 if not isinstance(value, Design): 

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

1213 

1214 if value is None: 

1215 # TODO: unlink VHDLLibrary from design 

1216 self._design = None 

1217 else: 

1218 if self._design is None: 

1219 self._design = value 

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

1221 elif self._design is not value: 

1222 # TODO: move VHDLLibrary to other design 

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

1224 self._design = value 

1225 else: 

1226 pass 

1227 

1228 if self._project is None: 

1229 self._project = value._project 

1230 elif self._project is not value._project: 

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

1232 

1233 @property 

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

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

1236 for file in self._files: 

1237 yield file 

1238 

1239 @property 

1240 def VHDLVersion(self) -> VHDLVersion: 

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

1242 if self._vhdlVersion is not None: 

1243 return self._vhdlVersion 

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

1245 return self._design.VHDLVersion 

1246 else: 

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

1248 

1249 @VHDLVersion.setter 

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

1251 self._vhdlVersion = value 

1252 

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

1254 library.parent = self 

1255 

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

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

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

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

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

1261 raise ex 

1262 

1263 self._files.append(vhdlFile) 

1264 

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

1266 for vhdlFile in vhdlFiles: 

1267 if not isinstance(vhdlFile, VHDLSourceFile): 

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

1269 

1270 self._files.append(vhdlFile) 

1271 

1272 @property 

1273 def FileCount(self) -> int: 

1274 """Returns number of files.""" 

1275 return len(self._files) 

1276 

1277 def __len__(self) -> int: 

1278 """ 

1279 Returns number of attributes set on this VHDL library. 

1280 

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

1282 """ 

1283 return len(self._attributes) 

1284 

1285 def __getitem__(self, key: Type[Attribute]) -> typing_Any: 

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

1287 

1288 :param key: The attribute type. 

1289 :returns: The attribute's value. 

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

1291 """ 

1292 if not issubclass(key, Attribute): 

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

1294 

1295 try: 

1296 return self._attributes[key] 

1297 except KeyError: 

1298 return key.resolve(self, key) 

1299 

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

1301 """ 

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

1303 

1304 :param key: The attribute type. 

1305 :param value: The attributes value. 

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

1307 """ 

1308 if not issubclass(key, Attribute): 

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

1310 

1311 self._attributes[key] = value 

1312 

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

1314 """ 

1315 Index access for deleting attributes on this VHDL library. 

1316 

1317 :param key: The attribute type. 

1318 """ 

1319 if not issubclass(key, Attribute): 

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

1321 

1322 del self._attributes[key] 

1323 

1324 def __str__(self) -> str: 

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

1326 return self._name 

1327 

1328 

1329@export 

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

1331 """ 

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

1333 

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

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

1336 exists. 

1337 

1338 :arg name: The design's name. 

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

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

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

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

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

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

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

1346 """ 

1347 

1348 _name: str 

1349 _topLevel: Nullable[str] 

1350 _project: Nullable['Project'] 

1351 _directory: pathlib_Path 

1352 _fileSets: Dict[str, FileSet] 

1353 _defaultFileSet: Nullable[FileSet] 

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

1355 

1356 _vhdlLibraries: Dict[str, VHDLLibrary] 

1357 _vhdlVersion: VHDLVersion 

1358 _verilogVersion: SystemVerilogVersion 

1359 _svVersion: SystemVerilogVersion 

1360 _srdlVersion: SystemRDLVersion 

1361 _externalVHDLLibraries: List 

1362 

1363 _vhdlLibraryDependencyGraph: Graph 

1364 _fileDependencyGraph: Graph 

1365 

1366 def __init__( 

1367 self, 

1368 name: str, 

1369 topLevel: Nullable[str] = None, 

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

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

1372 vhdlVersion: Nullable[VHDLVersion] = None, 

1373 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1374 svVersion: Nullable[SystemVerilogVersion] = None, 

1375 srdlVersion: Nullable[SystemRDLVersion] = None 

1376 ) -> None: 

1377 self._name = name 

1378 self._topLevel = topLevel 

1379 self._project = project 

1380 if project is not None: 

1381 project._designs[name] = self 

1382 self._directory = directory 

1383 self._fileSets = {} 

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

1385 self._attributes = {} 

1386 self._vhdlLibraries = {} 

1387 self._vhdlVersion = vhdlVersion 

1388 self._verilogVersion = verilogVersion 

1389 self._svVersion = svVersion 

1390 self._srdlVersion = srdlVersion 

1391 self._externalVHDLLibraries = [] 

1392 

1393 self._vhdlLibraryDependencyGraph = Graph() 

1394 self._fileDependencyGraph = Graph() 

1395 

1396 @property 

1397 def Name(self) -> str: 

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

1399 return self._name 

1400 

1401 @Name.setter 

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

1403 self._name = value 

1404 

1405 @property 

1406 def TopLevel(self) -> str: 

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

1408 return self._topLevel 

1409 

1410 @TopLevel.setter 

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

1412 self._topLevel = value 

1413 

1414 @property 

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

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

1417 return self._project 

1418 

1419 @Project.setter 

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

1421 self._project = value 

1422 

1423 @property 

1424 def Directory(self) -> pathlib_Path: 

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

1426 return self._directory 

1427 

1428 @Directory.setter 

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

1430 self._directory = value 

1431 

1432 @property 

1433 def ResolvedPath(self) -> pathlib_Path: 

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

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

1436 return self._directory.resolve() 

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

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

1439 

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

1441 return path 

1442 else: 

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

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

1445 else: 

1446 # TODO: message and exception type 

1447 raise Exception("") 

1448 

1449 @property 

1450 def DefaultFileSet(self) -> FileSet: 

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

1452 return self._defaultFileSet 

1453 

1454 @DefaultFileSet.setter 

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

1456 if isinstance(value, str): 

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

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

1459 

1460 self._defaultFileSet = self._fileSets[value] 

1461 elif isinstance(value, FileSet): 

1462 if value not in self.FileSets: 

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

1464 

1465 self._defaultFileSet = value 

1466 else: 

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

1468 

1469 # TODO: return generator with another method 

1470 @property 

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

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

1473 return self._fileSets 

1474 

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

1476 """ 

1477 Method returning the files of this design. 

1478 

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

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

1481 """ 

1482 if fileSet is None: 

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

1484 for file in fileSet.Files(fileType): 

1485 yield file 

1486 else: 

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

1488 try: 

1489 fileSet = self._fileSets[fileSet] 

1490 except KeyError as ex: 

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

1492 elif not isinstance(fileSet, FileSet): 

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

1494 

1495 for file in fileSet.Files(fileType): 

1496 yield file 

1497 

1498 def Validate(self) -> None: 

1499 """Validate this design.""" 

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

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

1502 

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

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

1505 try: 

1506 path = self.ResolvedPath 

1507 except Exception as ex: 

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

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

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

1511 if not path.is_dir(): 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}'s directory '{path}' is not a directory.") 

1513 

1514 if len(self._fileSets) == 0: 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}' has no fileset.") 

1516 try: 

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

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

1519 except KeyError as ex: 

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

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

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

1523 

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

1525 fileSet.Validate() 

1526 

1527 @property 

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

1529 return self._vhdlLibraries 

1530 

1531 @property 

1532 def VHDLVersion(self) -> VHDLVersion: 

1533 if self._vhdlVersion is not None: 

1534 return self._vhdlVersion 

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

1536 return self._project.VHDLVersion 

1537 else: 

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

1539 

1540 @VHDLVersion.setter 

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

1542 self._vhdlVersion = value 

1543 

1544 @property 

1545 def VerilogVersion(self) -> SystemVerilogVersion: 

1546 if self._verilogVersion is not None: 

1547 return self._verilogVersion 

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

1549 return self._project.VerilogVersion 

1550 else: 

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

1552 

1553 @VerilogVersion.setter 

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

1555 self._verilogVersion = value 

1556 

1557 @property 

1558 def SVVersion(self) -> SystemVerilogVersion: 

1559 if self._svVersion is not None: 

1560 return self._svVersion 

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

1562 return self._project.SVVersion 

1563 else: 

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

1565 

1566 @SVVersion.setter 

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

1568 self._svVersion = value 

1569 

1570 @property 

1571 def SRDLVersion(self) -> SystemRDLVersion: 

1572 if self._srdlVersion is not None: 

1573 return self._srdlVersion 

1574 elif self._project is not None: 

1575 return self._project.SRDLVersion 

1576 else: 

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

1578 

1579 @SRDLVersion.setter 

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

1581 self._srdlVersion = value 

1582 

1583 @property 

1584 def ExternalVHDLLibraries(self) -> List: 

1585 return self._externalVHDLLibraries 

1586 

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

1588 if not isinstance(fileSet, FileSet): 

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

1590 elif fileSet in self._fileSets: 

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

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

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

1594 

1595 self._fileSets[fileSet.Name] = fileSet 

1596 fileSet.Design = self 

1597 fileSet._parent = self 

1598 

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

1600 for fileSet in fileSets: 

1601 self.AddFileSet(fileSet) 

1602 

1603 @property 

1604 def FileSetCount(self) -> int: 

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

1606 return len(self._fileSets) 

1607 

1608 @property 

1609 def TotalFileSetCount(self) -> int: 

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

1611 fileSetCount = len(self._fileSets) 

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

1613 fileSetCount += fileSet.TotalFileSetCount 

1614 

1615 return fileSetCount 

1616 

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

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

1619 self._defaultFileSet.AddFile(file) 

1620 else: 

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

1622 

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

1624 for file in files: 

1625 self.AddFile(file) 

1626 

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

1628 if vhdlLibrary.Name in self._vhdlLibraries: 

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

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

1631 else: 

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

1633 

1634 

1635 def __len__(self) -> int: 

1636 """ 

1637 Returns number of attributes set on this design. 

1638 

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

1640 """ 

1641 return len(self._attributes) 

1642 

1643 def __getitem__(self, key: Type[Attribute]) -> typing_Any: 

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

1645 

1646 :param key: The attribute type. 

1647 :returns: The attribute's value. 

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

1649 """ 

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

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

1652 

1653 try: 

1654 return self._attributes[key] 

1655 except KeyError: 

1656 return key.resolve(self, key) 

1657 

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

1659 """ 

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

1661 

1662 :param key: The attribute type. 

1663 :param value: The attributes value. 

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

1665 """ 

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

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

1668 

1669 self._attributes[key] = value 

1670 

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

1672 """ 

1673 Index access for deleting attributes on this design. 

1674 

1675 :param key: The attribute type. 

1676 """ 

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

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

1679 

1680 del self._attributes[key] 

1681 

1682 def __str__(self) -> str: 

1683 return self._name 

1684 

1685 

1686@export 

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

1688 """ 

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

1690 

1691 :arg name: The project's name. 

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

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

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

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

1696 """ 

1697 

1698 _name: str 

1699 _rootDirectory: pathlib_Path 

1700 _designs: Dict[str, Design] 

1701 _defaultDesign: Design 

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

1703 

1704 _vhdlVersion: VHDLVersion 

1705 _verilogVersion: SystemVerilogVersion 

1706 _svVersion: SystemVerilogVersion 

1707 _srdlVersion: SystemRDLVersion 

1708 

1709 def __init__( 

1710 self, 

1711 name: str, 

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

1713 vhdlVersion: Nullable[VHDLVersion] = None, 

1714 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1715 svVersion: Nullable[SystemVerilogVersion] = None 

1716 ) -> None: 

1717 self._name = name 

1718 self._rootDirectory = rootDirectory 

1719 self._designs = {} 

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

1721 self._attributes = {} 

1722 self._vhdlVersion = vhdlVersion 

1723 self._verilogVersion = verilogVersion 

1724 self._svVersion = svVersion 

1725 

1726 @property 

1727 def Name(self) -> str: 

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

1729 return self._name 

1730 

1731 @property 

1732 def RootDirectory(self) -> pathlib_Path: 

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

1734 return self._rootDirectory 

1735 

1736 @RootDirectory.setter 

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

1738 self._rootDirectory = value 

1739 

1740 @property 

1741 def ResolvedPath(self) -> pathlib_Path: 

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

1743 path = self._rootDirectory.resolve() 

1744 if self._rootDirectory.is_absolute(): 

1745 return path 

1746 else: 

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

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

1749 

1750 # TODO: return generator with another method 

1751 @property 

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

1753 return self._designs 

1754 

1755 @property 

1756 def DefaultDesign(self) -> Design: 

1757 return self._defaultDesign 

1758 

1759 def Validate(self) -> None: 

1760 """Validate this project.""" 

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

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

1763 

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

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

1766 try: 

1767 path = self.ResolvedPath 

1768 except Exception as ex: 

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

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

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

1772 if not path.is_dir(): 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}'s directory '{path}' is not a directory.") 

1774 

1775 if len(self._designs) == 0: 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}' has no design.") 

1777 try: 

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

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

1780 except KeyError as ex: 

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

1782 

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

1784 design.Validate() 

1785 

1786 @property 

1787 def DesignCount(self) -> int: 

1788 """Returns number of designs.""" 

1789 return len(self._designs) 

1790 

1791 @property 

1792 def VHDLVersion(self) -> VHDLVersion: 

1793 # TODO: check for None and return exception 

1794 return self._vhdlVersion 

1795 

1796 @VHDLVersion.setter 

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

1798 self._vhdlVersion = value 

1799 

1800 @property 

1801 def VerilogVersion(self) -> SystemVerilogVersion: 

1802 # TODO: check for None and return exception 

1803 return self._verilogVersion 

1804 

1805 @VerilogVersion.setter 

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

1807 self._verilogVersion = value 

1808 

1809 @property 

1810 def SVVersion(self) -> SystemVerilogVersion: 

1811 # TODO: check for None and return exception 

1812 return self._svVersion 

1813 

1814 @SVVersion.setter 

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

1816 self._svVersion = value 

1817 

1818 @property 

1819 def SRDLVersion(self) -> SystemRDLVersion: 

1820 # TODO: check for None and return exception 

1821 return self._srdlVersion 

1822 

1823 @SRDLVersion.setter 

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

1825 self._srdlVersion = value 

1826 

1827 def __len__(self) -> int: 

1828 """ 

1829 Returns number of attributes set on this project. 

1830 

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

1832 """ 

1833 return len(self._attributes) 

1834 

1835 def __getitem__(self, key: Type[Attribute]) -> typing_Any: 

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

1837 

1838 :param key: The attribute type. 

1839 :returns: The attribute's value. 

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

1841 """ 

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

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

1844 

1845 try: 

1846 return self._attributes[key] 

1847 except KeyError: 

1848 return key.resolve(self, key) 

1849 

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

1851 """ 

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

1853 

1854 :param key: The attribute type. 

1855 :param value: The attributes value. 

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

1857 """ 

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

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

1860 

1861 self._attributes[key] = value 

1862 

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

1864 """ 

1865 Index access for deleting attributes on this project. 

1866 

1867 :param key: The attribute type. 

1868 """ 

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

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

1871 

1872 del self._attributes[key] 

1873 

1874 def __str__(self) -> str: 

1875 return self._name