Coverage for pyEDAA/OSVVM/Project/__init__.py: 87%
557 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-28 23:17 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-28 23:17 +0000
1# ==================================================================================================================== #
2# _____ ____ _ _ ___ ______ ____ ____ __ #
3# _ __ _ _| ____| _ \ / \ / \ / _ \/ ___\ \ / /\ \ / / \/ | #
4# | '_ \| | | | _| | | | |/ _ \ / _ \ | | | \___ \\ \ / / \ \ / /| |\/| | #
5# | |_) | |_| | |___| |_| / ___ \ / ___ \ | |_| |___) |\ V / \ V / | | | | #
6# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)___/|____/ \_/ \_/ |_| |_| #
7# |_| |___/ #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2025-2025 Patrick Lehmann - Boetzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31from pathlib import Path
32from typing import Optional as Nullable, List, Dict, Mapping, Iterable, TypeVar, Generic, Generator
34from pyTooling.Common import getFullyQualifiedName
35from pyTooling.Decorators import readonly, export
36from pyTooling.MetaClasses import ExtendedType
37from pyVHDLModel import VHDLVersion
39from pyEDAA.OSVVM import OSVVMException
42__all__ = ["osvvmContext"]
45_ParentType = TypeVar("_ParentType", bound="Base")
48@export
49class Base(Generic[_ParentType], metaclass=ExtendedType, slots=True):
50 _parent: Nullable[_ParentType]
52 def __init__(self, parent: Nullable[_ParentType] = None) -> None:
53 self._parent = parent
55 @readonly
56 def Parent(self) -> _ParentType:
57 return self._parent
60@export
61class Named(Base[_ParentType], Generic[_ParentType]):
62 _name: str
64 def __init__(
65 self,
66 name: str,
67 parent: Nullable[_ParentType] = None
68 ) -> None:
69 super().__init__(parent)
71 if not isinstance(name, str): # pragma: no cover
72 ex = TypeError(f"Parameter 'name' is not a string.")
73 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.")
74 raise ex
76 self._name = name
78 @readonly
79 def Name(self) -> str:
80 return self._name
82 def __repr__(self) -> str:
83 return f"{self.__class__.__name__}: {self._name}"
86@export
87class Option(metaclass=ExtendedType, slots=True):
88 pass
91@export
92class NoNullRangeWarning(Option):
93 def __init__(self) -> None:
94 super().__init__()
96 def __repr__(self) -> str:
97 return "NoNullRangeWarning"
100@export
101class SourceFile(Base[_ParentType], Generic[_ParentType]):
102 """A base-class describing any source file (VHDL, Verilog, ...) supported by OSVVM Scripts."""
104 _path: Path
106 def __init__(
107 self,
108 path: Path,
109 parent: Nullable[Base] = None
110 ) -> None:
111 super().__init__(parent)
113 if not isinstance(path, Path): # pragma: no cover
114 ex = TypeError(f"Parameter 'path' is not a Path.")
115 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.")
116 raise ex
118 self._path = path
120 @readonly
121 def Path(self) -> Path:
122 """
123 Read-only property to access the path to the sourcefile.
125 :returns: The sourcefile's path.
126 """
127 return self._path
129 def __repr__(self) -> str:
130 return f"{self.__class__.__name__}: {self._path}"
133@export
134class XDCConstraintFile(SourceFile):
135 _scopeToRef: str
136 _scopeToCell: str
138 def __init__(self, path: Path, scopeToRef: Nullable[str], scopeToCell: Nullable[str]) -> None:
139 super().__init__(path)
141 if scopeToRef is not None and not isinstance(scopeToRef, str): 141 ↛ 142line 141 didn't jump to line 142 because the condition on line 141 was never true
142 ex = TypeError(f"Parameter 'scopeToRef' is not a str.")
143 ex.add_note(f"Got type '{getFullyQualifiedName(scopeToRef)}'.")
144 raise ex
146 self._scopeToRef = scopeToRef
148 if scopeToCell is not None and not isinstance(scopeToCell, str): 148 ↛ 149line 148 didn't jump to line 149 because the condition on line 148 was never true
149 ex = TypeError(f"Parameter 'scopeToCell' is not a str.")
150 ex.add_note(f"Got type '{getFullyQualifiedName(scopeToCell)}'.")
151 raise ex
153 self._scopeToCell = scopeToCell
155 def __repr__(self) -> str:
156 properties = []
157 if self._scopeToRef is not None:
158 properties.append(f"ScopeToRef={self._scopeToRef}")
159 if self._scopeToCell is not None:
160 properties.append(f"ScopeToCell={self._scopeToCell}")
162 props = f" {{{', '.join(properties)}}}" if len(properties) > 0 else ""
164 return f"{super().__repr__()}{props}"
167@export
168class VHDLSourceFile(SourceFile["VHDLLibrary"]):
169 _vhdlVersion: VHDLVersion
170 _noNullRangeWarning: Nullable[bool]
171 _associatedFiles: List[XDCConstraintFile]
173 def __init__(
174 self,
175 path: Path,
176 vhdlVersion: VHDLVersion = VHDLVersion.VHDL2008,
177 vhdlLibrary: Nullable["VHDLLibrary"] = None,
178 noNullRangeWarning: Nullable[bool] = None,
179 associatedFiles: Nullable[Iterable[SourceFile]] = None
180 ) -> None:
181 if vhdlLibrary is None:
182 super().__init__(path)
183 elif isinstance(vhdlLibrary, VHDLLibrary):
184 super().__init__(path, vhdlLibrary)
185 vhdlLibrary._files.append(self)
186 else: # pragma: no cover
187 ex = TypeError(f"Parameter 'vhdlLibrary' is not a Library.")
188 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.")
189 raise ex
191 if not isinstance(vhdlVersion, VHDLVersion): # pragma: no cover
192 ex = TypeError(f"Parameter 'vhdlVersion' is not a VHDLVersion.")
193 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlVersion)}'.")
194 raise ex
196 self._vhdlVersion = vhdlVersion
198 if noNullRangeWarning is not None and not isinstance(noNullRangeWarning, bool): 198 ↛ 199line 198 didn't jump to line 199 because the condition on line 198 was never true
199 ex = TypeError(f"Parameter 'noNullRangeWarning' is not a boolean.")
200 ex.add_note(f"Got type '{getFullyQualifiedName(noNullRangeWarning)}'.")
201 raise ex
203 self._noNullRangeWarning = noNullRangeWarning
204 self._associatedFiles = [] if associatedFiles is None else [f for f in associatedFiles]
206 @readonly
207 def VHDLLibrary(self) -> Nullable["VHDLLibrary"]:
208 return self._parent
210 @property
211 def VHDLVersion(self) -> VHDLVersion:
212 return self._vhdlVersion
214 @VHDLVersion.setter
215 def VHDLVersion(self, value: VHDLVersion) -> None:
216 if not isinstance(value, VHDLVersion): 216 ↛ 217line 216 didn't jump to line 217 because the condition on line 216 was never true
217 ex = TypeError(f"Parameter 'value' is not a VHDLVersion.")
218 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
219 raise ex
221 self._vhdlVersion = value
223 @property
224 def NoNullRangeWarning(self) -> bool:
225 return self._noNullRangeWarning
227 @NoNullRangeWarning.setter
228 def NoNullRangeWarning(self, value: bool) -> None:
229 if value is not None and not isinstance(value, bool):
230 ex = TypeError(f"Parameter 'value' is not a boolean.")
231 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
232 raise ex
234 self._noNullRangeWarning = value
236 @property
237 def AssociatedFiles(self) -> List[SourceFile]:
238 return self._associatedFiles
240 def __repr__(self) -> str:
241 options = ""
242 if self._noNullRangeWarning is not None: 242 ↛ 243line 242 didn't jump to line 243 because the condition on line 242 was never true
243 options += f", NoNullRangeWarning"
244 return f"VHDLSourceFile: {self._path} ({self._vhdlVersion}{options})"
247@export
248class VHDLLibrary(Named["Build"]):
249 """A VHDL library collecting multiple VHDL files containing VHDL design units."""
251 _files: List[VHDLSourceFile]
253 def __init__(
254 self,
255 name: str,
256 vhdlFiles: Nullable[Iterable[VHDLSourceFile]] = None,
257 build: Nullable["Build"] = None
258 ) -> None:
259 if build is None:
260 super().__init__(name, None)
261 elif isinstance(build, Build):
262 super().__init__(name, build)
263 build._vhdlLibraries[name] = self
264 else: # pragma: no cover
265 ex = TypeError(f"Parameter 'build' is not a Build.")
266 ex.add_note(f"Got type '{getFullyQualifiedName(build)}'.")
267 raise ex
269 self._files = []
270 if vhdlFiles is None:
271 pass
272 elif isinstance(vhdlFiles, Iterable):
273 for vhdlFile in vhdlFiles:
274 vhdlFile._parent = self
275 self._files.append(vhdlFile)
276 else: # pragma: no cover
277 ex = TypeError(f"Parameter 'vhdlFiles' is not an iterable of VHDLSourceFile.")
278 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlFiles)}'.")
279 raise ex
281 @readonly
282 def Build(self) -> Nullable["Build"]:
283 return self._parent
285 @readonly
286 def Files(self) -> List[SourceFile]:
287 return self._files
289 def AddFile(self, file: VHDLSourceFile) -> None:
290 if not isinstance(file, VHDLSourceFile): # pragma: no cover
291 ex = TypeError(f"Parameter 'file' is not a VHDLSourceFile.")
292 ex.add_note(f"Got type '{getFullyQualifiedName(file)}'.")
293 raise ex
295 file._parent = self
296 self._files.append(file)
298 def __repr__(self) -> str:
299 return f"VHDLLibrary: {self._name}"
302@export
303class GenericValue(Option):
304 _name: str
305 _value: str
307 def __init__(
308 self,
309 name: str,
310 value: str
311 ) -> None:
312 super().__init__()
314 if not isinstance(name, str): # pragma: no cover
315 ex = TypeError(f"Parameter 'name' is not a string.")
316 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.")
317 raise ex
319 self._name = name
321 if not isinstance(value, str): # pragma: no cover
322 ex = TypeError(f"Parameter 'value' is not a string.")
323 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.")
324 raise ex
326 self._value = value
328 @readonly
329 def Name(self) -> str:
330 return self._name
332 @readonly
333 def Value(self) -> str:
334 return self._value
336 def __repr__(self) -> str:
337 return f"{self._name} = {self._value}"
340@export
341class ConstraintFile(Option):
342 _path: Path
343 _scopeToRef: Nullable[str]
344 _scopeToCell: Nullable[str]
346 def __init__(
347 self,
348 path: Path,
349 scopeToRef: Nullable[str] = None,
350 scopeToCell: Nullable[str] = None
351 ) -> None:
352 super().__init__()
354 if not isinstance(path, Path): # pragma: no cover
355 ex = TypeError(f"Parameter 'path' is not a Path.")
356 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.")
357 raise ex
359 self._path = path
361 if scopeToRef is not None and not isinstance(scopeToRef, str): 361 ↛ 362line 361 didn't jump to line 362 because the condition on line 361 was never true
362 ex = TypeError(f"Parameter 'scopeToRef' is not a str.")
363 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.")
364 raise ex
366 self._scopeToRef = scopeToRef
368 if scopeToCell is not None and not isinstance(scopeToCell, str): 368 ↛ 369line 368 didn't jump to line 369 because the condition on line 368 was never true
369 ex = TypeError(f"Parameter 'scopeToCell' is not a str.")
370 ex.add_note(f"Got type '{getFullyQualifiedName(path)}'.")
371 raise ex
373 self._scopeToCell = scopeToCell
375 @readonly
376 def Path(self) -> Path:
377 return self._path
379 @readonly
380 def ScopeToRef(self) -> Nullable[str]:
381 return self._scopeToRef
383 @readonly
384 def ScopeToCell(self) -> Nullable[str]:
385 return self._scopeToCell
387 def __repr__(self) -> str:
388 properties = []
389 if self._scopeToRef is not None:
390 properties.append(f"ScopeToRef={self._scopeToRef}")
391 if self._scopeToCell is not None:
392 properties.append(f"ScopeToCell={self._scopeToCell}")
394 props = f" {{{', '.join(properties)}}}" if len(properties) > 0 else ""
396 return f"{self._path}{props}"
399@export
400class ScopeToRef(Option):
401 _reference: str
403 def __init__(
404 self,
405 reference: str
406 ) -> None:
407 super().__init__()
409 if not isinstance(reference, str): # pragma: no cover
410 ex = TypeError(f"Parameter 'reference' is not a string.")
411 ex.add_note(f"Got type '{getFullyQualifiedName(reference)}'.")
412 raise ex
414 self._reference = reference
416 @readonly
417 def Reference(self) -> str:
418 return self._reference
420 def __repr__(self) -> str:
421 return f"{self._reference}"
424@export
425class ScopeToCell(Option):
426 _cell: str
428 def __init__(
429 self,
430 cell: str
431 ) -> None:
432 super().__init__()
434 if not isinstance(cell, str): # pragma: no cover
435 ex = TypeError(f"Parameter 'cell' is not a string.")
436 ex.add_note(f"Got type '{getFullyQualifiedName(cell)}'.")
437 raise ex
439 self._cell = cell
441 @readonly
442 def Cell(self) -> str:
443 return self._cell
445 def __repr__(self) -> str:
446 return f"{self._cell}"
449@export
450class Testcase(Named["Testsuite"]):
451 _toplevelName: Nullable[str]
452 _generics: Dict[str, str]
454 def __init__(
455 self,
456 name: str,
457 toplevelName: Nullable[str] = None,
458 generics: Nullable[Iterable[GenericValue] | Mapping[str, str]] = None,
459 testsuite: Nullable["Testsuite"] = None
460 ) -> None:
461 if testsuite is None:
462 super().__init__(name, None)
463 elif isinstance(testsuite, Testsuite):
464 super().__init__(name, testsuite)
465 testsuite._testcases[name] = self
466 else: # pragma: no cover
467 ex = TypeError(f"Parameter 'testsuite' is not a Testsuite.")
468 ex.add_note(f"Got type '{getFullyQualifiedName(testsuite)}'.")
469 raise ex
471 if not (toplevelName is None or isinstance(toplevelName, str)): # pragma: no cover
472 ex = TypeError(f"Parameter 'toplevelName' is not a string.")
473 ex.add_note(f"Got type '{getFullyQualifiedName(toplevelName)}'.")
474 raise ex
476 self._toplevelName = toplevelName
478 self._generics = {}
479 if generics is None:
480 pass
481 elif isinstance(generics, Mapping):
482 for key, value in generics.items():
483 self._generics[key] = value
484 elif isinstance(generics, Iterable):
485 for item in generics:
486 self._generics[item._name] = item._value
487 else: # pragma: no cover
488 ex = TypeError(f"Parameter 'generics' is not an iterable of GenericValue nor a dictionary of strings.")
489 ex.add_note(f"Got type '{getFullyQualifiedName(generics)}'.")
490 raise ex
492 @readonly
493 def Testsuite(self) -> "Testsuite":
494 return self._parent
496 @readonly
497 def ToplevelName(self) -> str:
498 return self._toplevelName
500 @readonly
501 def Generics(self) -> Dict[str, str]:
502 return self._generics
504 def SetToplevel(self, toplevelName: str) -> None:
505 if not isinstance(toplevelName, str): # pragma: no cover
506 ex = TypeError(f"Parameter 'toplevelName' is not a string.")
507 ex.add_note(f"Got type '{getFullyQualifiedName(toplevelName)}'.")
508 raise ex
510 self._toplevelName = toplevelName
512 def AddGeneric(self, genericValue: GenericValue):
513 if not isinstance(genericValue, GenericValue): # pragma: no cover
514 ex = TypeError(f"Parameter 'genericValue' is not a GenericValue.")
515 ex.add_note(f"Got type '{getFullyQualifiedName(genericValue)}'.")
516 raise ex
518 self._generics[genericValue._name] = genericValue._value
520 def __repr__(self) -> str:
521 generics = f" - [{', '.join([f'{n}={v}' for n,v in self._generics.items()])}]" if len(self._generics) > 0 else ""
522 return f"Testcase: {self._name}{generics}"
525@export
526class Testsuite(Named["Build"]):
527 _testcases: Dict[str, Testcase]
529 def __init__(
530 self,
531 name: str,
532 testcases: Nullable[Iterable[Testcase] | Mapping[str, Testcase]] = None,
533 build: Nullable["Build"] = None
534 ) -> None:
535 if build is None:
536 super().__init__(name, None)
537 elif isinstance(build, Build):
538 super().__init__(name, build)
539 build._testsuites[name] = self
540 else: # pragma: no cover
541 ex = TypeError(f"Parameter 'build' is not a Build.")
542 ex.add_note(f"Got type '{getFullyQualifiedName(build)}'.")
543 raise ex
545 self._testcases = {}
546 if testcases is None:
547 pass
548 elif isinstance(testcases, Mapping):
549 for key, value in testcases.items():
550 value._parent = self
551 self._testcases[key] = value
552 elif isinstance(testcases, Iterable):
553 for item in testcases:
554 item._parent = self
555 self._testcases[item._name] = item
556 else: # pragma: no cover
557 ex = TypeError(f"Parameter 'testcases' is not an iterable of Testcase nor a mapping of Testcase.")
558 ex.add_note(f"Got type '{getFullyQualifiedName(testcases)}'.")
559 raise ex
561 @readonly
562 def Build(self) -> Nullable["Build"]:
563 return self._parent
565 @readonly
566 def Testcases(self) -> Dict[str, Testcase]:
567 return self._testcases
569 def AddTestcase(self, testcase: Testcase) -> None:
570 if not isinstance(testcase, Testcase): # pragma: no cover
571 ex = TypeError(f"Parameter 'testcase' is not a Testcase.")
572 ex.add_note(f"Got type '{getFullyQualifiedName(testcase)}'.")
573 raise ex
575 testcase._parent = self
576 self._testcases[testcase._name] = testcase
578 def __repr__(self) -> str:
579 return f"Testsuite: {self._name}"
582@export
583class BuildName(Option):
584 _name: str
586 def __init__(
587 self,
588 name: str,
589 ) -> None:
590 super().__init__()
592 if not isinstance(name, str): # pragma: no cover
593 ex = TypeError(f"Parameter 'name' is not a string.")
594 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.")
595 raise ex
597 self._name = name
599 @readonly
600 def Name(self) -> str:
601 return self._name
603 def __repr__(self) -> str:
604 return f"BuildName: {self._name}"
607@export
608class Build(Named["Project"]):
609 _includedFiles: List[Path]
610 _vhdlLibraries: Dict[str, VHDLLibrary]
611 _testsuites: Dict[str, Testsuite]
613 def __init__(
614 self,
615 name: str,
616 vhdlLibraries: Nullable[Iterable[VHDLLibrary] | Mapping[str, VHDLLibrary]] = None,
617 testsuites: Nullable[Iterable[Testsuite] | Mapping[str, Testsuite]] = None,
618 project: Nullable[Base] = None
619 ) -> None:
620 if project is None:
621 super().__init__(name, None)
622 elif isinstance(project, Project):
623 super().__init__(name, project)
624 project._builds[name] = self
625 else: # pragma: no cover
626 ex = TypeError(f"Parameter 'project' is not a Project.")
627 ex.add_note(f"Got type '{getFullyQualifiedName(project)}'.")
628 raise ex
630 self._includedFiles = []
631 self._vhdlLibraries = {}
632 if vhdlLibraries is None:
633 pass
634 elif isinstance(vhdlLibraries, Mapping):
635 for key, value in vhdlLibraries.items():
636 value._parent = self
637 self._vhdlLibraries[key] = value
638 elif isinstance(vhdlLibraries, Iterable):
639 for item in vhdlLibraries:
640 item._parent = self
641 self._vhdlLibraries[item._name] = item
642 else: # pragma: no cover
643 ex = TypeError(f"Parameter 'libraries' is not an iterable of VHDLLibrary nor a mapping of VHDLLibrary.")
644 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibraries)}'.")
645 raise ex
647 self._testsuites = {}
648 if testsuites is None:
649 pass
650 elif isinstance(testsuites, Mapping):
651 for key, value in testsuites.items():
652 value._parent = self
653 self._testsuites[key] = value
654 elif isinstance(testsuites, Iterable):
655 for item in testsuites:
656 item._parent = self
657 self._testsuites[item._name] = item
658 else: # pragma: no cover
659 ex = TypeError(f"Parameter 'testsuites' is not an iterable of Testsuite nor a mapping of Testsuite.")
660 ex.add_note(f"Got type '{getFullyQualifiedName(testsuites)}'.")
661 raise ex
663 @readonly
664 def Project(self) -> Nullable["Project"]:
665 return self._parent
667 @readonly
668 def IncludedFiles(self) -> Generator[Path, None, None]:
669 return (file for file in self._includedFiles)
671 @readonly
672 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]:
673 return self._vhdlLibraries
675 @readonly
676 def Testsuites(self) -> Dict[str, Testsuite]:
677 return self._testsuites
679 def AddVHDLLibrary(self, vhdlLibrary: VHDLLibrary) -> None:
680 if not isinstance(vhdlLibrary, VHDLLibrary): # pragma: no cover
681 ex = TypeError(f"Parameter 'vhdlLibrary' is not a VHDLLibrary.")
682 ex.add_note(f"Got type '{getFullyQualifiedName(vhdlLibrary)}'.")
683 raise ex
685 vhdlLibrary._parent = self
686 self._vhdlLibraries[vhdlLibrary._name] = vhdlLibrary
688 def AddTestsuite(self, testsuite: Testsuite) -> None:
689 if not isinstance(testsuite, Testsuite): # pragma: no cover
690 ex = TypeError(f"Parameter 'testsuite' is not a Testsuite.")
691 ex.add_note(f"Got type '{getFullyQualifiedName(testsuite)}'.")
692 raise ex
694 testsuite._parent = self
695 self._testsuites[testsuite._name] = testsuite
697 def __repr__(self) -> str:
698 return f"Build: {self._name}"
701@export
702class Project(Named[None]):
703 _builds: Dict[str, Build]
705 def __init__(
706 self,
707 name: str,
708 builds: Nullable[Iterable[Build] | Mapping[str, Build]] = None
709 ) -> None:
710 super().__init__(name, None)
712 self._builds = {}
713 if builds is None:
714 pass
715 elif isinstance(builds, Mapping):
716 for key, value in builds.items():
717 value._parent = self
718 self._builds[key] = value
719 elif isinstance(builds, Iterable):
720 for item in builds:
721 item._parent = self
722 self._builds[item._name] = item
723 else: # pragma: no cover
724 ex = TypeError(f"Parameter 'builds' is not an iterable of Build nor a mapping of Build.")
725 ex.add_note(f"Got type '{getFullyQualifiedName(builds)}'.")
726 raise ex
728 @readonly
729 def Builds(self) -> Dict[str, Build]:
730 return self._builds
732 @readonly
733 def IncludedFiles(self) -> Generator[Path, None, None]:
734 for build in self._builds.values():
735 yield from build.IncludedFiles
737 def AddBuild(self, build: Build) -> None:
738 if not isinstance(build, Build): # pragma: no cover
739 ex = TypeError(f"Parameter 'build' is not a Build.")
740 ex.add_note(f"Got type '{getFullyQualifiedName(build)}'.")
741 raise ex
743 build._parent = self
744 self._builds[build._name] = build
746 def __repr__(self) -> str:
747 return f"Project: {self._name}"
750@export
751class Context(Base):
752 # _tcl: TclEnvironment
754 _processor: "OsvvmProFileProcessor"
755 _lastException: Exception
757 _workingDirectory: Path
758 _currentDirectory: Path
759 _includedFiles: List[Path]
761 _vhdlversion: VHDLVersion
763 _vhdlLibraries: Dict[str, VHDLLibrary]
764 _vhdlLibrary: Nullable[VHDLLibrary]
766 _testsuites: Dict[str, Testsuite]
767 _testsuite: Nullable[Testsuite]
768 _testcase: Nullable[Testcase]
769 _options: Dict[int, Option]
771 _builds: Dict[str, Build]
772 _build: Nullable[Build]
774 def __init__(self) -> None:
775 super().__init__()
777 self._processor = None
778 self._lastException = None
780 self._workingDirectory = Path.cwd()
781 self._currentDirectory = self._workingDirectory
782 self._includedFiles = []
784 self._vhdlversion = VHDLVersion.VHDL2008
786 self._vhdlLibrary = None
787 self._vhdlLibraries = {}
789 self._testcase = None
790 self._testsuite = None
791 self._testsuites = {}
792 self._options = {}
794 self._build = None
795 self._builds = {}
797 def Clear(self) -> None:
798 self._processor = None
799 self._lastException = None
801 self._workingDirectory = Path.cwd()
802 self._currentDirectory = self._workingDirectory
803 self._includedFiles = []
805 self._vhdlversion = VHDLVersion.VHDL2008
807 self._vhdlLibrary = None
808 self._vhdlLibraries = {}
810 self._testcase = None
811 self._testsuite = None
812 self._testsuites = {}
813 self._options = {}
815 self._build = None
816 self._builds = {}
818 @readonly
819 def Processor(self): # -> "Tk":
820 return self._processor
822 @property
823 def LastException(self) -> Exception:
824 lastException = self._lastException
825 self._lastException = None
826 return lastException
828 @LastException.setter
829 def LastException(self, value: Exception) -> None:
830 self._lastException = value
832 @readonly
833 def WorkingDirectory(self) -> Path:
834 return self._workingDirectory
836 @readonly
837 def CurrentDirectory(self) -> Path:
838 return self._currentDirectory
840 @property
841 def VHDLVersion(self) -> VHDLVersion:
842 return self._vhdlversion
844 @VHDLVersion.setter
845 def VHDLVersion(self, value: VHDLVersion) -> None:
846 self._vhdlversion = value
848 @readonly
849 def IncludedFiles(self) -> List[Path]:
850 return self._includedFiles
852 @readonly
853 def VHDLLibraries(self) -> Dict[str, VHDLLibrary]:
854 return self._vhdlLibraries
856 @readonly
857 def VHDLLibrary(self) -> VHDLLibrary:
858 return self._vhdlLibrary
860 @readonly
861 def Testsuites(self) -> Dict[str, Testsuite]:
862 return self._testsuites
864 @readonly
865 def Testsuite(self) -> Testsuite:
866 return self._testsuite
868 @readonly
869 def TestCase(self) -> Testcase:
870 return self._testcase
872 @readonly
873 def Build(self) -> Build:
874 return self._build
876 @readonly
877 def Builds(self) -> Dict[str, Build]:
878 return self._builds
880 def ToProject(self, projectName: str) -> Project:
881 project = Project(projectName, self._builds)
883 return project
885 def BeginBuild(self, buildName: str) -> Build:
886 if len(self._vhdlLibraries) > 0: 886 ↛ 887line 886 didn't jump to line 887 because the condition on line 886 was never true
887 raise OSVVMException(f"VHDL libraries have been created outside of an OSVVM build script.")
888 if len(self._testsuites) > 0: 888 ↛ 889line 888 didn't jump to line 889 because the condition on line 888 was never true
889 raise OSVVMException(f"Testsuites have been created outside of an OSVVM build script.")
891 build = Build(buildName)
892 build._vhdlLibraries = self._vhdlLibraries
893 build._testsuites = self._testsuites
895 self._build = build
896 self._builds[buildName] = build
898 return build
900 def EndBuild(self) -> Build:
901 build = self._build
903 self._vhdlLibrary = None
904 self._vhdlLibraries = {}
905 self._testcase = None
906 self._testsuite = None
907 self._testsuites = {}
908 self._build = None
910 # TODO: should this be handled in LoadBuildFile ?
911 self._currentDirectory = self._workingDirectory
913 return build
915 def IncludeFile(self, proFileOrBuildDirectory: Path) -> Path:
916 if not isinstance(proFileOrBuildDirectory, Path): # pragma: no cover
917 ex = TypeError(f"Parameter 'proFileOrBuildDirectory' is not a Path.")
918 ex.add_note(f"Got type '{getFullyQualifiedName(proFileOrBuildDirectory)}'.")
919 self._lastException = ex
920 raise ex
922 if proFileOrBuildDirectory.is_absolute(): 922 ↛ 923line 922 didn't jump to line 923 because the condition on line 922 was never true
923 ex = OSVVMException(f"Absolute path '{proFileOrBuildDirectory}' not supported.")
924 self._lastException = ex
925 raise ex
927 path = (self._currentDirectory / proFileOrBuildDirectory).resolve()
928 if path.is_file():
929 if path.suffix == ".pro": 929 ↛ 933line 929 didn't jump to line 933 because the condition on line 929 was always true
930 self._currentDirectory = path.parent.relative_to(self._workingDirectory, walk_up=True)
931 proFile = self._currentDirectory / path.name
932 else:
933 ex = OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a *.pro file.")
934 self._lastException = ex
935 raise ex
936 elif path.is_dir():
937 self._currentDirectory = path
938 proFile = path / "build.pro"
939 if not proFile.exists():
940 proFile = path / f"{path.name}.pro"
941 if not proFile.exists(): # pragma: no cover
942 ex = OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a build directory.")
943 ex.__cause__ = FileNotFoundError(path / "build.pro")
944 self._lastException = ex
945 raise ex
946 else: # pragma: no cover
947 ex = OSVVMException(f"Path '{proFileOrBuildDirectory}' is not a *.pro file or build directory.")
948 self._lastException = ex
949 raise ex
951 self._includedFiles.append(proFile)
952 return proFile
954 def EvaluateFile(self, proFile: Path) -> None:
955 self._processor.EvaluateProFile(proFile)
957 def SetLibrary(self, name: str):
958 try:
959 self._vhdlLibrary = self._vhdlLibraries[name]
960 except KeyError:
961 self._vhdlLibrary = VHDLLibrary(name, build=self._build)
962 self._vhdlLibraries[name] = self._vhdlLibrary
964 def AddVHDLFile(self, vhdlFile: VHDLSourceFile) -> None:
965 if self._vhdlLibrary is None:
966 self.SetLibrary("default")
968 vhdlFile.VHDLVersion = self._vhdlversion
969 self._vhdlLibrary.AddFile(vhdlFile)
971 def SetTestsuite(self, testsuiteName: str):
972 try:
973 self._testsuite = self._testsuites[testsuiteName]
974 except KeyError:
975 self._testsuite = Testsuite(testsuiteName)
976 self._testsuites[testsuiteName] = self._testsuite
978 def AddTestcase(self, testName: str) -> TestCase:
979 if self._testsuite is None:
980 self.SetTestsuite("default")
982 self._testcase = Testcase(testName)
983 self._testsuite._testcases[testName] = self._testcase
985 return self._testcase
987 def SetTestcaseToplevel(self, toplevel: str) -> TestCase:
988 if self._testcase is None: 988 ↛ 989line 988 didn't jump to line 989 because the condition on line 988 was never true
989 ex = OSVVMException("Can't set testcase toplevel, because no testcase was setup.")
990 self._lastException = ex
991 raise ex
993 self._testcase.SetToplevel(toplevel)
995 return self._testcase
997 def AddOption(self, option: Option) -> int:
998 optionID = id(option)
999 self._options[optionID] = option
1001 return optionID
1004osvvmContext: Context = Context()
1005"""
1006Global OSVVM processing context.
1008:type: Context
1009"""