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

1015 statements  

« prev     ^ index     » next       coverage.py v7.15.1, created at 2026-07-13 18:08 +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.4" 

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

39__project_url__ = "https://github.com/edaa-org/pyEDAA.ProjectModel" 

40__documentation_url__ = "https://edaa-org.github.io/pyEDAA.ProjectModel" 

41__issue_tracker_url__ = "https://GitHub.com/edaa-org/pyEDAA.ProjectModel/issues" 

42 

43from os.path import relpath as path_relpath 

44from pathlib import Path as pathlib_Path 

45from sys import version_info 

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

47 

48from pyTooling.Common import getFullyQualifiedName 

49from pyTooling.Decorators import export 

50from pyTooling.MetaClasses import ExtendedType 

51from pyTooling.Graph import Graph, Vertex 

52from pySVModel import SystemVerilogVersion 

53from pyVHDLModel import VHDLVersion 

54from pySystemRDLModel import SystemRDLVersion 

55 

56 

57@export 

58class Attribute(metaclass=ExtendedType): 

59 KEY: str 

60 VALUE_TYPE: typing_Any 

61 

62 @staticmethod 

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

64 if isinstance(obj, File): 

65 return obj._fileSet[key] 

66 elif isinstance(obj, FileSet): 

67 return obj._design[key] 

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

69 return obj._project[key] 

70 else: 

71 raise Exception("Resolution error") 

72 

73 

74@export 

75class FileType(ExtendedType): 

76 """ 

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

78 

79 Modifications done by this meta-class: 

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

81 """ 

82 

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

84 Any: 'FileType' 

85 

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

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

88 cls.Any = cls 

89 

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

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

92 cls.FileTypes[className] = fileType 

93 return fileType 

94 

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

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

97 return cls.FileTypes[item] 

98 else: 

99 return super().__getattribute__(item) 

100 

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

102 return issubclass(item, cls) 

103 

104 

105@export 

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

107 """ 

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

109 for all derived file classes. 

110 

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

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

113 while creating a file. 

114 

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

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

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

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

119 """ 

120 

121 _path: pathlib_Path 

122 _fileType: 'FileType' 

123 _project: Nullable['Project'] 

124 _design: Nullable['Design'] 

125 _fileSet: Nullable['FileSet'] 

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

127 

128 def __init__( 

129 self, 

130 path: pathlib_Path, 

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

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

133 fileSet: Nullable["FileSet"] = None 

134 ) -> None: 

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

136 self._path = path 

137 if project is not None: 

138 self._project = project 

139 self._design = design 

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

141 self.FileSet = fileSet 

142 elif design is not None: 

143 self._project = design._project 

144 self._design = design 

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

146 elif fileSet is not None: 

147 design = fileSet._design 

148 if design is not None: 

149 self._project = design._project 

150 else: 

151 self._project = None 

152 self._design = design 

153 self.FileSet = fileSet 

154 else: 

155 self._project = None 

156 self._design = None 

157 self._fileSet = None 

158 

159 self._attributes = {} 

160 self._registerAttributes() 

161 

162 def _registerAttributes(self) -> None: 

163 pass 

164 

165 @property 

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

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

168 return self._fileType 

169 

170 @property 

171 def Path(self) -> pathlib_Path: 

172 """ 

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

174 

175 :returns: The file's path. 

176 """ 

177 return self._path 

178 

179 # TODO: setter? 

180 

181 @property 

182 def ResolvedPath(self) -> pathlib_Path: 

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

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

185 return self._path.resolve() 

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

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

188 

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

190 return path 

191 else: 

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

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

194 else: 

195 # TODO: message and exception type 

196 raise Exception("") 

197 

198 @property 

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

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

201 return self._project 

202 

203 @Project.setter 

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

205 self._project = value 

206 

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

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

209 

210 @property 

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

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

213 return self._design 

214 

215 @Design.setter 

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

217 self._design = value 

218 

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

220 self._design.DefaultFileSet.AddFile(self) 

221 

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

223 self._project = value._project 

224 elif self._project is not value._project: 

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

226 

227 @property 

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

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

230 return self._fileSet 

231 

232 @FileSet.setter 

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

234 self._fileSet = value 

235 value._files.append(self) 

236 

237 def Validate(self) -> None: 

238 """Validate this file.""" 

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

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

241 try: 

242 path = self.ResolvedPath 

243 except Exception as ex: 

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

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

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

247 if not path.is_file(): 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}' (={path}) is not a file.") 

249 

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

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

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

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

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

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

256 

257 def __len__(self) -> int: 

258 """ 

259 Returns number of attributes set on this file. 

260 

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

262 """ 

263 return len(self._attributes) 

264 

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

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

267 

268 :param key: The attribute type. 

269 :returns: The attribute's value. 

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

271 """ 

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

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

274 

275 try: 

276 return self._attributes[key] 

277 except KeyError: 

278 try: 

279 return key.resolve(self, key) 

280 except KeyError: 

281 attribute = key() 

282 self._attributes[key] = attribute 

283 return attribute 

284 

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

286 """ 

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

288 

289 :param key: The attribute type. 

290 :param value: The attributes value. 

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

292 """ 

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

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

295 

296 self._attributes[key] = value 

297 

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

299 """ 

300 Index access for deleting attributes on this file. 

301 

302 :param key: The attribute type. 

303 """ 

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

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

306 

307 del self._attributes[key] 

308 

309 def __str__(self) -> str: 

310 return f"{self._path}" 

311 

312 

313FileTypes = File 

314 

315 

316@export 

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

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

319 

320 

321@export 

322class XMLContent(HumanReadableContent, mixin=True): 

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

324 

325 

326@export 

327class YAMLContent(HumanReadableContent, mixin=True): 

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

329 

330 

331@export 

332class JSONContent(HumanReadableContent, mixin=True): 

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

334 

335 

336@export 

337class INIContent(HumanReadableContent, mixin=True): 

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

339 

340 

341@export 

342class TOMLContent(HumanReadableContent, mixin=True): 

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

344 

345 

346@export 

347class TCLContent(HumanReadableContent, mixin=True): 

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

349 

350 

351@export 

352class SDCContent(TCLContent, mixin=True): 

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

354 

355 

356@export 

357class PythonContent(HumanReadableContent, mixin=True): 

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

359 

360 

361@export 

362class TextFile(File, HumanReadableContent): 

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

364 

365 

366@export 

367class LogFile(File, HumanReadableContent): 

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

369 

370 

371@export 

372class XMLFile(File, XMLContent): 

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

374 

375 

376@export 

377class SourceFile(File): 

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

379 

380 

381@export 

382class HDLSourceFile(SourceFile): 

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

384 

385 

386@export 

387class RDLSourceFile(SourceFile): 

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

389 

390 

391@export 

392class NetlistFile(SourceFile): 

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

394 

395 

396@export 

397class EDIFNetlistFile(NetlistFile): 

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

399 

400 

401@export 

402class TCLSourceFile(SourceFile, TCLContent): 

403 """A TCL source file.""" 

404 

405 

406@export 

407class VHDLSourceFile(HDLSourceFile, HumanReadableContent): 

408 """ 

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

410 

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

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

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

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

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

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

417 """ 

418 

419 _vhdlLibrary: Nullable['VHDLLibrary'] 

420 _vhdlVersion: VHDLVersion 

421 

422 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: 

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

424 

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

426 if design is not None: 

427 try: 

428 vhdlLibrary = design.VHDLLibraries[vhdlLibrary] 

429 except KeyError as ex: 

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

431 elif project is not None: 

432 try: 

433 vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary] 

434 except KeyError as ex: 

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

436 else: 

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

438 elif isinstance(vhdlLibrary, VHDLLibrary): 

439 self._vhdlLibrary = vhdlLibrary 

440 vhdlLibrary.AddFile(self) 

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

442 self._vhdlLibrary = None 

443 else: 

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

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

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

447 raise ex 

448 

449 self._vhdlVersion = vhdlVersion 

450 

451 def Validate(self) -> None: 

452 """Validate this VHDL source file.""" 

453 super().Validate() 

454 

455 try: 

456 _ = self.VHDLLibrary 

457 except Exception as ex: 

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

459 try: 

460 _ = self.VHDLVersion 

461 except Exception as ex: 

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

463 

464 @property 

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

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

467 if self._vhdlLibrary is not None: 

468 return self._vhdlLibrary 

469 elif self._fileSet is not None: 

470 return self._fileSet.VHDLLibrary 

471 else: 

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

473 

474 @VHDLLibrary.setter 

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

476 self._vhdlLibrary = value 

477 value._files.append(self) 

478 

479 @property 

480 def VHDLVersion(self) -> VHDLVersion: 

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

482 if self._vhdlVersion is not None: 

483 return self._vhdlVersion 

484 elif self._fileSet is not None: 

485 return self._fileSet.VHDLVersion 

486 else: 

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

488 

489 @VHDLVersion.setter 

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

491 self._vhdlVersion = value 

492 

493 def __repr__(self) -> str: 

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

495 

496 

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

498 @property 

499 def VerilogVersion(self) -> SystemVerilogVersion: 

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

501 if self._version is not None: 

502 return self._version 

503 elif self._fileSet is not None: 

504 return self._fileSet.VerilogVersion 

505 else: 

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

507 

508 @VerilogVersion.setter 

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

510 self._version = value 

511 

512 

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

514 @property 

515 def SVVersion(self) -> SystemVerilogVersion: 

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

517 if self._version is not None: 

518 return self._version 

519 elif self._fileSet is not None: 

520 return self._fileSet.SVVersion 

521 else: 

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

523 

524 @SVVersion.setter 

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

526 self._version = value 

527 

528 

529@export 

530class VerilogBaseFile(HDLSourceFile, HumanReadableContent): 

531 _version: SystemVerilogVersion 

532 

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

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

535 

536 self._version = version 

537 

538 

539@export 

540class VerilogSourceFile(VerilogBaseFile, VerilogMixIn): 

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

542 

543 

544@export 

545class VerilogHeaderFile(VerilogBaseFile, VerilogMixIn): 

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

547 

548 

549@export 

550class SystemVerilogBaseFile(VerilogBaseFile): 

551 ... 

552 

553 

554@export 

555class SystemVerilogSourceFile(SystemVerilogBaseFile, SystemVerilogMixIn): 

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

557 

558 

559@export 

560class SystemVerilogHeaderFile(SystemVerilogBaseFile, SystemVerilogMixIn): 

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

562 

563 

564@export 

565class SystemRDLSourceFile(RDLSourceFile, HumanReadableContent): 

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

567 

568 _srdlVersion: SystemRDLVersion 

569 

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

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

572 

573 self._srdlVersion = srdlVersion 

574 

575 @property 

576 def SystemRDLVersion(self) -> SystemRDLVersion: 

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

578 if self._srdlVersion is not None: 

579 return self._srdlVersion 

580 elif self._fileSet is not None: 

581 return self._fileSet.SRDLVersion 

582 else: 

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

584 

585 @SystemRDLVersion.setter 

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

587 self._srdlVersion = value 

588 

589 

590@export 

591class PythonSourceFile(SourceFile, PythonContent): 

592 """A Python source file.""" 

593 

594 

595# TODO: move to a Cocotb module 

596@export 

597class CocotbPythonFile(PythonSourceFile): 

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

599 

600 

601@export 

602class ConstraintFile(File, HumanReadableContent): 

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

604 

605 

606@export 

607class ProjectFile(File): 

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

609 

610 

611@export 

612class CSourceFile(SourceFile): 

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

614 

615 

616@export 

617class CppSourceFile(SourceFile): 

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

619 

620 

621@export 

622class SettingFile(File): 

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

624 

625 

626@export 

627class SimulationAnalysisFile(File): 

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

629 

630 

631@export 

632class SimulationElaborationFile(File): 

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

634 

635 

636@export 

637class SimulationStartFile(File): 

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

639 

640 

641@export 

642class SimulationRunFile(File): 

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

644 

645 

646@export 

647class WaveformConfigFile(File): 

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

649 

650 

651@export 

652class WaveformDatabaseFile(File): 

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

654 

655 

656@export 

657class WaveformExchangeFile(File): 

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

659 

660 

661@export 

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

663 """ 

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

665 

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

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

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

669 fileset. 

670 

671 :arg name: Name of this fileset. 

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

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

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

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

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

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

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

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

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

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

682 """ 

683 

684 _name: str 

685 _topLevel: Nullable[str] 

686 _project: Nullable['Project'] 

687 _design: Nullable['Design'] 

688 _directory: pathlib_Path 

689 _parent: Nullable['FileSet'] 

690 _fileSets: Dict[str, 'FileSet'] 

691 _files: List[File] 

692 _set: Set 

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

694 _vhdlLibraries: Dict[str, 'VHDLLibrary'] 

695 _vhdlLibrary: 'VHDLLibrary' 

696 _vhdlVersion: VHDLVersion 

697 _verilogVersion: SystemVerilogVersion 

698 _svVersion: SystemVerilogVersion 

699 _srdlVersion: SystemRDLVersion 

700 

701 def __init__( 

702 self, 

703 name: str, 

704 topLevel: Nullable[str] = None, 

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

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

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

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

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

710 vhdlVersion: Nullable[VHDLVersion] = None, 

711 verilogVersion: Nullable[SystemVerilogVersion] = None, 

712 svVersion: Nullable[SystemVerilogVersion] = None, 

713 srdlVersion: Nullable[SystemRDLVersion] = None 

714 ) -> None: 

715 self._name = name 

716 self._topLevel = topLevel 

717 if project is not None: 

718 self._project = project 

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

720 

721 elif design is not None: 

722 self._project = design._project 

723 self._design = design 

724 else: 

725 self._project = None 

726 self._design = None 

727 self._directory = directory 

728 self._parent = parent 

729 self._fileSets = {} 

730 self._files = [] 

731 self._set = set() 

732 

733 if design is not None: 

734 design._fileSets[name] = self 

735 

736 self._attributes = {} 

737 self._vhdlLibraries = {} 

738 

739 # TODO: handle if vhdlLibrary is a string 

740 self._vhdlLibrary = vhdlLibrary 

741 self._vhdlVersion = vhdlVersion 

742 self._verilogVersion = verilogVersion 

743 self._svVersion = svVersion 

744 self._srdlVersion = srdlVersion 

745 

746 @property 

747 def Name(self) -> str: 

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

749 return self._name 

750 

751 @Name.setter 

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

753 self._name = value 

754 

755 @property 

756 def TopLevel(self) -> str: 

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

758 return self._topLevel 

759 

760 @TopLevel.setter 

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

762 self._topLevel = value 

763 

764 @property 

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

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

767 return self._project 

768 

769 @Project.setter 

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

771 self._project = value 

772 

773 @property 

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

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

776 if self._design is not None: 

777 return self._design 

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

779 return self._parent.Design 

780 else: 

781 return None 

782 # TODO: raise exception instead 

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

784 

785 @Design.setter 

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

787 self._design = value 

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

789 self._project = value._project 

790 elif self._project is not value._project: 

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

792 

793 @property 

794 def Directory(self) -> pathlib_Path: 

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

796 return self._directory 

797 

798 @Directory.setter 

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

800 self._directory = value 

801 

802 @property 

803 def ResolvedPath(self) -> pathlib_Path: 

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

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

806 return self._directory.resolve() 

807 else: 

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

809 directory = self._parent.ResolvedPath 

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

811 directory = self._design.ResolvedPath 

812 elif self._project is not None: 

813 directory = self._project.ResolvedPath 

814 else: 

815 # TODO: message and exception type 

816 raise Exception("") 

817 

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

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

820 return directory 

821 else: 

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

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

824 

825 @property 

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

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

828 return self._parent 

829 

830 @Parent.setter 

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

832 self._parent = value 

833 value._fileSets[self._name] = self 

834 # TODO: check it it already exists 

835 # QUESTION: make an Add fileset method? 

836 

837 @property 

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

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

840 return self._fileSets 

841 

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

843 """ 

844 Method returning the files of this fileset. 

845 

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

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

848 """ 

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

850 for file in self._files: 

851 if file.FileType in fileType: 

852 yield file 

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

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

855 for file in fileSet.Files(fileType): 

856 yield file 

857 for file in self._files: 

858 if file.FileType in fileType: 

859 yield file 

860 else: 

861 if isinstance(fileSet, str): 

862 fileSetName = fileSet 

863 try: 

864 fileSet = self._fileSets[fileSetName] 

865 except KeyError as ex: 

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

867 elif not isinstance(fileSet, FileSet): 

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

869 

870 for file in fileSet.Files(fileType): 

871 yield file 

872 

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

874 """ 

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

876 

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

878 """ 

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

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

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

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

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

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

885 

886 self._fileSets[fileSet.Name] = fileSet 

887 fileSet._parent = self 

888 

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

890 """ 

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

892 

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

894 """ 

895 for fileSet in fileSets: 

896 self.AddFileSet(fileSet) 

897 

898 @property 

899 def FileSetCount(self) -> int: 

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

901 return len(self._fileSets) 

902 

903 @property 

904 def TotalFileSetCount(self) -> int: 

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

906 fileSetCount = len(self._fileSets) 

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

908 fileSetCount += fileSet.TotalFileSetCount 

909 

910 return fileSetCount 

911 

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

913 """ 

914 Method to add a single file to this fileset. 

915 

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

917 """ 

918 if not isinstance(file, File): 

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

920 elif file._fileSet is not None: 

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

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

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

924 raise ex 

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

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

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

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

929 raise ex 

930 

931 self._files.append(file) 

932 self._set.add(file) 

933 file._fileSet = self 

934 

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

936 """ 

937 Method to add a multiple files to this fileset. 

938 

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

940 """ 

941 for file in files: 

942 self.AddFile(file) 

943 

944 @property 

945 def FileCount(self) -> int: 

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

947 return len(self._files) 

948 

949 @property 

950 def TotalFileCount(self) -> int: 

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

952 fileCount = len(self._files) 

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

954 fileCount += fileSet.FileCount 

955 

956 return fileCount 

957 

958 def Validate(self) -> None: 

959 """Validate this fileset.""" 

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

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

962 

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

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

965 try: 

966 path = self.ResolvedPath 

967 except Exception as ex: 

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

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

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

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

973 

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

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

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

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

978 

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

980 fileSet.Validate() 

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

982 file.Validate() 

983 

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

985 if name in self._vhdlLibraries: 

986 return self._vhdlLibraries[name] 

987 elif name in self._design._vhdlLibraries: 

988 library = self._design._vhdlLibraries[name] 

989 self._vhdlLibraries[name] = library 

990 return library 

991 else: 

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

993 self._vhdlLibraries[name] = library 

994 return library 

995 

996 @property 

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

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

999 if self._vhdlLibrary is not None: 

1000 return self._vhdlLibrary 

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

1002 return self._parent.VHDLLibrary 

1003 elif self._design is not None: 

1004 return self._design.VHDLLibrary # FIXME: no library property. add a DefaultVHDLLibrary property 

1005 else: 

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

1007 

1008 @VHDLLibrary.setter 

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

1010 self._vhdlLibrary = value 

1011 

1012 @property 

1013 def VHDLVersion(self) -> VHDLVersion: 

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

1015 if self._vhdlVersion is not None: 

1016 return self._vhdlVersion 

1017 elif self._parent is not None: 

1018 return self._parent.VHDLVersion 

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

1020 return self._design.VHDLVersion 

1021 else: 

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

1023 

1024 @VHDLVersion.setter 

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

1026 self._vhdlVersion = value 

1027 

1028 @property 

1029 def VerilogVersion(self) -> SystemVerilogVersion: 

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

1031 if self._verilogVersion is not None: 

1032 return self._verilogVersion 

1033 elif self._parent is not None: 

1034 return self._parent.VerilogVersion 

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

1036 return self._design.VerilogVersion 

1037 else: 

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

1039 

1040 @VerilogVersion.setter 

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

1042 self._verilogVersion = value 

1043 

1044 @property 

1045 def SVVersion(self) -> SystemVerilogVersion: 

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

1047 if self._svVersion is not None: 

1048 return self._svVersion 

1049 elif self._parent is not None: 

1050 return self._parent.SVVersion 

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

1052 return self._design.SVVersion 

1053 else: 

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

1055 

1056 @SVVersion.setter 

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

1058 self._svVersion = value 

1059 

1060 @property 

1061 def SRDLVersion(self) -> SystemRDLVersion: 

1062 if self._srdlVersion is not None: 

1063 return self._srdlVersion 

1064 elif self._parent is not None: 

1065 return self._parent.SRDLVersion 

1066 elif self._design is not None: 

1067 return self._design.SRDLVersion 

1068 else: 

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

1070 

1071 @SRDLVersion.setter 

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

1073 self._srdlVersion = value 

1074 

1075 def __len__(self) -> int: 

1076 """ 

1077 Returns number of attributes set on this fileset. 

1078 

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

1080 """ 

1081 return len(self._attributes) 

1082 

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

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

1085 

1086 :param key: The attribute type. 

1087 :returns: The attribute's value. 

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

1089 """ 

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

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

1092 

1093 try: 

1094 return self._attributes[key] 

1095 except KeyError: 

1096 return key.resolve(self, key) 

1097 

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

1099 """ 

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

1101 

1102 :param key: The attribute type. 

1103 :param value: The attributes value. 

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

1105 """ 

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

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

1108 

1109 self._attributes[key] = value 

1110 

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

1112 """ 

1113 Index access for deleting attributes on this fileset. 

1114 

1115 :param key: The attribute type. 

1116 """ 

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

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

1119 

1120 del self._attributes[key] 

1121 

1122 def __str__(self) -> str: 

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

1124 return self._name 

1125 

1126 

1127@export 

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

1129 """ 

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

1131 

1132 :arg name: The VHDL libraries' name. 

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

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

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

1136 """ 

1137 

1138 _name: str 

1139 _project: Nullable['Project'] 

1140 _design: Nullable['Design'] 

1141 _files: List[File] 

1142 _vhdlVersion: VHDLVersion 

1143 

1144 _attributes: Dict[Attribute, typing_Any] 

1145 _dependencyNode: Vertex 

1146 

1147 def __init__( 

1148 self, 

1149 name: str, 

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

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

1152 vhdlVersion: Nullable[VHDLVersion] = None 

1153 ) -> None: 

1154 self._name = name 

1155 self._attributes = {} 

1156 

1157 if project is not None: 

1158 self._project = project 

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

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

1161 

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

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

1164 else: 

1165 self._design._vhdlLibraries[name] = self 

1166 

1167 elif design is not None: 

1168 self._project = design._project 

1169 self._design = design 

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

1171 

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

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

1174 else: 

1175 design._vhdlLibraries[name] = self 

1176 

1177 else: 

1178 self._project = None 

1179 self._design = None 

1180 self._dependencyNode = None 

1181 

1182 self._files = [] 

1183 self._vhdlVersion = vhdlVersion 

1184 

1185 @property 

1186 def Name(self) -> str: 

1187 return self._name 

1188 

1189 @property 

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

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

1192 return self._project 

1193 

1194 @Project.setter 

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

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

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

1198 

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

1200 # TODO: unlink VHDLLibrary from project 

1201 self._project = None 

1202 else: 

1203 self._project = value 

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

1205 self._design = value._defaultDesign 

1206 

1207 @property 

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

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

1210 return self._design 

1211 

1212 @Design.setter 

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

1214 if not isinstance(value, Design): 

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

1216 

1217 if value is None: 

1218 # TODO: unlink VHDLLibrary from design 

1219 self._design = None 

1220 else: 

1221 if self._design is None: 

1222 self._design = value 

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

1224 elif self._design is not value: 

1225 # TODO: move VHDLLibrary to other design 

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

1227 self._design = value 

1228 else: 

1229 pass 

1230 

1231 if self._project is None: 

1232 self._project = value._project 

1233 elif self._project is not value._project: 

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

1235 

1236 @property 

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

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

1239 for file in self._files: 

1240 yield file 

1241 

1242 @property 

1243 def VHDLVersion(self) -> VHDLVersion: 

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

1245 if self._vhdlVersion is not None: 

1246 return self._vhdlVersion 

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

1248 return self._design.VHDLVersion 

1249 else: 

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

1251 

1252 @VHDLVersion.setter 

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

1254 self._vhdlVersion = value 

1255 

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

1257 library.parent = self 

1258 

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

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

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

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

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

1264 raise ex 

1265 

1266 self._files.append(vhdlFile) 

1267 

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

1269 for vhdlFile in vhdlFiles: 

1270 if not isinstance(vhdlFile, VHDLSourceFile): 

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

1272 

1273 self._files.append(vhdlFile) 

1274 

1275 @property 

1276 def FileCount(self) -> int: 

1277 """Returns number of files.""" 

1278 return len(self._files) 

1279 

1280 def __len__(self) -> int: 

1281 """ 

1282 Returns number of attributes set on this VHDL library. 

1283 

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

1285 """ 

1286 return len(self._attributes) 

1287 

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

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

1290 

1291 :param key: The attribute type. 

1292 :returns: The attribute's value. 

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

1294 """ 

1295 if not issubclass(key, Attribute): 

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

1297 

1298 try: 

1299 return self._attributes[key] 

1300 except KeyError: 

1301 return key.resolve(self, key) 

1302 

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

1304 """ 

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

1306 

1307 :param key: The attribute type. 

1308 :param value: The attributes value. 

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

1310 """ 

1311 if not issubclass(key, Attribute): 

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

1313 

1314 self._attributes[key] = value 

1315 

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

1317 """ 

1318 Index access for deleting attributes on this VHDL library. 

1319 

1320 :param key: The attribute type. 

1321 """ 

1322 if not issubclass(key, Attribute): 

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

1324 

1325 del self._attributes[key] 

1326 

1327 def __str__(self) -> str: 

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

1329 return self._name 

1330 

1331 

1332@export 

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

1334 """ 

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

1336 

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

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

1339 exists. 

1340 

1341 :arg name: The design's name. 

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

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

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

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

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

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

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

1349 """ 

1350 

1351 _name: str 

1352 _topLevel: Nullable[str] 

1353 _project: Nullable['Project'] 

1354 _directory: pathlib_Path 

1355 _fileSets: Dict[str, FileSet] 

1356 _defaultFileSet: Nullable[FileSet] 

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

1358 

1359 _vhdlLibraries: Dict[str, VHDLLibrary] 

1360 _vhdlVersion: VHDLVersion 

1361 _verilogVersion: SystemVerilogVersion 

1362 _svVersion: SystemVerilogVersion 

1363 _srdlVersion: SystemRDLVersion 

1364 _externalVHDLLibraries: List 

1365 

1366 _vhdlLibraryDependencyGraph: Graph 

1367 _fileDependencyGraph: Graph 

1368 

1369 def __init__( 

1370 self, 

1371 name: str, 

1372 topLevel: Nullable[str] = None, 

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

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

1375 vhdlVersion: Nullable[VHDLVersion] = None, 

1376 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1377 svVersion: Nullable[SystemVerilogVersion] = None, 

1378 srdlVersion: Nullable[SystemRDLVersion] = None 

1379 ) -> None: 

1380 self._name = name 

1381 self._topLevel = topLevel 

1382 self._project = project 

1383 if project is not None: 

1384 project._designs[name] = self 

1385 self._directory = directory 

1386 self._fileSets = {} 

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

1388 self._attributes = {} 

1389 self._vhdlLibraries = {} 

1390 self._vhdlVersion = vhdlVersion 

1391 self._verilogVersion = verilogVersion 

1392 self._svVersion = svVersion 

1393 self._srdlVersion = srdlVersion 

1394 self._externalVHDLLibraries = [] 

1395 

1396 self._vhdlLibraryDependencyGraph = Graph() 

1397 self._fileDependencyGraph = Graph() 

1398 

1399 @property 

1400 def Name(self) -> str: 

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

1402 return self._name 

1403 

1404 @Name.setter 

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

1406 self._name = value 

1407 

1408 @property 

1409 def TopLevel(self) -> str: 

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

1411 return self._topLevel 

1412 

1413 @TopLevel.setter 

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

1415 self._topLevel = value 

1416 

1417 @property 

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

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

1420 return self._project 

1421 

1422 @Project.setter 

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

1424 self._project = value 

1425 

1426 @property 

1427 def Directory(self) -> pathlib_Path: 

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

1429 return self._directory 

1430 

1431 @Directory.setter 

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

1433 self._directory = value 

1434 

1435 @property 

1436 def ResolvedPath(self) -> pathlib_Path: 

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

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

1439 return self._directory.resolve() 

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

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

1442 

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

1444 return path 

1445 else: 

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

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

1448 else: 

1449 # TODO: message and exception type 

1450 raise Exception("") 

1451 

1452 @property 

1453 def DefaultFileSet(self) -> FileSet: 

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

1455 return self._defaultFileSet 

1456 

1457 @DefaultFileSet.setter 

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

1459 if isinstance(value, str): 

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

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

1462 

1463 self._defaultFileSet = self._fileSets[value] 

1464 elif isinstance(value, FileSet): 

1465 if value not in self.FileSets: 

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

1467 

1468 self._defaultFileSet = value 

1469 else: 

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

1471 

1472 # TODO: return generator with another method 

1473 @property 

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

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

1476 return self._fileSets 

1477 

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

1479 """ 

1480 Method returning the files of this design. 

1481 

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

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

1484 """ 

1485 if fileSet is None: 

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

1487 for file in fileSet.Files(fileType): 

1488 yield file 

1489 else: 

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

1491 try: 

1492 fileSet = self._fileSets[fileSet] 

1493 except KeyError as ex: 

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

1495 elif not isinstance(fileSet, FileSet): 

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

1497 

1498 for file in fileSet.Files(fileType): 

1499 yield file 

1500 

1501 def Validate(self) -> None: 

1502 """Validate this design.""" 

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

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

1505 

1506 if self._directory is None: 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}' has no directory.") 

1508 try: 

1509 path = self.ResolvedPath 

1510 except Exception as ex: 

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

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

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

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

1516 

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

1519 try: 

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

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

1522 except KeyError as ex: 

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

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

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

1526 

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

1528 fileSet.Validate() 

1529 

1530 @property 

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

1532 return self._vhdlLibraries 

1533 

1534 @property 

1535 def VHDLVersion(self) -> VHDLVersion: 

1536 if self._vhdlVersion is not None: 

1537 return self._vhdlVersion 

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

1539 return self._project.VHDLVersion 

1540 else: 

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

1542 

1543 @VHDLVersion.setter 

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

1545 self._vhdlVersion = value 

1546 

1547 @property 

1548 def VerilogVersion(self) -> SystemVerilogVersion: 

1549 if self._verilogVersion is not None: 

1550 return self._verilogVersion 

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

1552 return self._project.VerilogVersion 

1553 else: 

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

1555 

1556 @VerilogVersion.setter 

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

1558 self._verilogVersion = value 

1559 

1560 @property 

1561 def SVVersion(self) -> SystemVerilogVersion: 

1562 if self._svVersion is not None: 

1563 return self._svVersion 

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

1565 return self._project.SVVersion 

1566 else: 

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

1568 

1569 @SVVersion.setter 

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

1571 self._svVersion = value 

1572 

1573 @property 

1574 def SRDLVersion(self) -> SystemRDLVersion: 

1575 if self._srdlVersion is not None: 

1576 return self._srdlVersion 

1577 elif self._project is not None: 

1578 return self._project.SRDLVersion 

1579 else: 

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

1581 

1582 @SRDLVersion.setter 

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

1584 self._srdlVersion = value 

1585 

1586 @property 

1587 def ExternalVHDLLibraries(self) -> List: 

1588 return self._externalVHDLLibraries 

1589 

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

1591 if not isinstance(fileSet, FileSet): 

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

1593 elif fileSet in self._fileSets: 

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

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

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

1597 

1598 self._fileSets[fileSet.Name] = fileSet 

1599 fileSet.Design = self 

1600 fileSet._parent = self 

1601 

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

1603 for fileSet in fileSets: 

1604 self.AddFileSet(fileSet) 

1605 

1606 @property 

1607 def FileSetCount(self) -> int: 

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

1609 return len(self._fileSets) 

1610 

1611 @property 

1612 def TotalFileSetCount(self) -> int: 

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

1614 fileSetCount = len(self._fileSets) 

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

1616 fileSetCount += fileSet.TotalFileSetCount 

1617 

1618 return fileSetCount 

1619 

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

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

1622 self._defaultFileSet.AddFile(file) 

1623 else: 

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

1625 

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

1627 for file in files: 

1628 self.AddFile(file) 

1629 

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

1631 if vhdlLibrary.Name in self._vhdlLibraries: 

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

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

1634 else: 

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

1636 

1637 

1638 def __len__(self) -> int: 

1639 """ 

1640 Returns number of attributes set on this design. 

1641 

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

1643 """ 

1644 return len(self._attributes) 

1645 

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

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

1648 

1649 :param key: The attribute type. 

1650 :returns: The attribute's value. 

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

1652 """ 

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

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

1655 

1656 try: 

1657 return self._attributes[key] 

1658 except KeyError: 

1659 return key.resolve(self, key) 

1660 

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

1662 """ 

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

1664 

1665 :param key: The attribute type. 

1666 :param value: The attributes value. 

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

1668 """ 

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

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

1671 

1672 self._attributes[key] = value 

1673 

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

1675 """ 

1676 Index access for deleting attributes on this design. 

1677 

1678 :param key: The attribute type. 

1679 """ 

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

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

1682 

1683 del self._attributes[key] 

1684 

1685 def __str__(self) -> str: 

1686 return self._name 

1687 

1688 

1689@export 

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

1691 """ 

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

1693 

1694 :arg name: The project's name. 

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

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

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

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

1699 """ 

1700 

1701 _name: str 

1702 _rootDirectory: pathlib_Path 

1703 _designs: Dict[str, Design] 

1704 _defaultDesign: Design 

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

1706 

1707 _vhdlVersion: VHDLVersion 

1708 _verilogVersion: SystemVerilogVersion 

1709 _svVersion: SystemVerilogVersion 

1710 _srdlVersion: SystemRDLVersion 

1711 

1712 def __init__( 

1713 self, 

1714 name: str, 

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

1716 vhdlVersion: Nullable[VHDLVersion] = None, 

1717 verilogVersion: Nullable[SystemVerilogVersion] = None, 

1718 svVersion: Nullable[SystemVerilogVersion] = None 

1719 ) -> None: 

1720 self._name = name 

1721 self._rootDirectory = rootDirectory 

1722 self._designs = {} 

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

1724 self._attributes = {} 

1725 self._vhdlVersion = vhdlVersion 

1726 self._verilogVersion = verilogVersion 

1727 self._svVersion = svVersion 

1728 

1729 @property 

1730 def Name(self) -> str: 

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

1732 return self._name 

1733 

1734 @property 

1735 def RootDirectory(self) -> pathlib_Path: 

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

1737 return self._rootDirectory 

1738 

1739 @RootDirectory.setter 

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

1741 self._rootDirectory = value 

1742 

1743 @property 

1744 def ResolvedPath(self) -> pathlib_Path: 

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

1746 path = self._rootDirectory.resolve() 

1747 if self._rootDirectory.is_absolute(): 

1748 return path 

1749 else: 

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

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

1752 

1753 # TODO: return generator with another method 

1754 @property 

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

1756 return self._designs 

1757 

1758 @property 

1759 def DefaultDesign(self) -> Design: 

1760 return self._defaultDesign 

1761 

1762 def Validate(self) -> None: 

1763 """Validate this project.""" 

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

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

1766 

1767 if self._rootDirectory is None: 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}' has no root directory.") 

1769 try: 

1770 path = self.ResolvedPath 

1771 except Exception as ex: 

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

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

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

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

1777 

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

1780 try: 

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

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

1783 except KeyError as ex: 

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

1785 

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

1787 design.Validate() 

1788 

1789 @property 

1790 def DesignCount(self) -> int: 

1791 """Returns number of designs.""" 

1792 return len(self._designs) 

1793 

1794 @property 

1795 def VHDLVersion(self) -> VHDLVersion: 

1796 # TODO: check for None and return exception 

1797 return self._vhdlVersion 

1798 

1799 @VHDLVersion.setter 

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

1801 self._vhdlVersion = value 

1802 

1803 @property 

1804 def VerilogVersion(self) -> SystemVerilogVersion: 

1805 # TODO: check for None and return exception 

1806 return self._verilogVersion 

1807 

1808 @VerilogVersion.setter 

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

1810 self._verilogVersion = value 

1811 

1812 @property 

1813 def SVVersion(self) -> SystemVerilogVersion: 

1814 # TODO: check for None and return exception 

1815 return self._svVersion 

1816 

1817 @SVVersion.setter 

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

1819 self._svVersion = value 

1820 

1821 @property 

1822 def SRDLVersion(self) -> SystemRDLVersion: 

1823 # TODO: check for None and return exception 

1824 return self._srdlVersion 

1825 

1826 @SRDLVersion.setter 

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

1828 self._srdlVersion = value 

1829 

1830 def __len__(self) -> int: 

1831 """ 

1832 Returns number of attributes set on this project. 

1833 

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

1835 """ 

1836 return len(self._attributes) 

1837 

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

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

1840 

1841 :param key: The attribute type. 

1842 :returns: The attribute's value. 

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

1844 """ 

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

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

1847 

1848 try: 

1849 return self._attributes[key] 

1850 except KeyError: 

1851 return key.resolve(self, key) 

1852 

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

1854 """ 

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

1856 

1857 :param key: The attribute type. 

1858 :param value: The attributes value. 

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

1860 """ 

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

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

1863 

1864 self._attributes[key] = value 

1865 

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

1867 """ 

1868 Index access for deleting attributes on this project. 

1869 

1870 :param key: The attribute type. 

1871 """ 

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

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

1874 

1875 del self._attributes[key] 

1876 

1877 def __str__(self) -> str: 

1878 return self._name