diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0896e81d..0e75a2d3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,15 +1,29 @@ version: 2 updates: -- package-ecosystem: pip - directory: "/" - target-branch: dev - commit-message: - prefix: "[Dependabot]" - labels: + # Maintain Python packages + - package-ecosystem: "pip" + directory: "/" + target-branch: dev + commit-message: + prefix: "[Dependabot]" + labels: - Dependencies - assignees: - - Paebbels - reviewers: - - Paebbels - schedule: - interval: daily + reviewers: + - Paebbels + - Umarcor + schedule: + interval: "daily" # Checks on Monday trough Friday. + + # Maintain GitHub Action runners + - package-ecosystem: "github-actions" + directory: "/" + target-branch: dev + commit-message: + prefix: "[Dependabot]" + labels: + - Dependencies + reviewers: + - Paebbels + - Umarcor + schedule: + interval: "weekly" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5e16226d..ac698f89 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1,5 @@ # New Features - + * tbd # Changes @@ -9,3 +9,8 @@ # Bug Fixes * tbd + +---------- +# Related PRs: + +* tbd diff --git a/.github/workflows/Pipeline.yml b/.github/workflows/Pipeline.yml index a862b778..c0acfaa4 100644 --- a/.github/workflows/Pipeline.yml +++ b/.github/workflows/Pipeline.yml @@ -9,57 +9,55 @@ on: jobs: Params: - uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 + uses: pyTooling/Actions/.github/workflows/Parameters.yml@dev with: name: pyEDAA.ProjectModel - python_version_list: "3.7 3.8 3.9 3.10" UnitTesting: - uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r0 + uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@dev needs: - Params with: jobs: ${{ needs.Params.outputs.python_jobs }} - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }} + artifact: ${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }} Coverage: - uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0 + uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@dev needs: - Params with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} + python_version: ${{ needs.Params.outputs.python_version }} + artifact: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }} secrets: codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }} StaticTypeCheck: - uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0 + uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@dev needs: - Params with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} + python_version: ${{ needs.Params.outputs.python_version }} commands: | cd pyEDAA mypy --html-report ../htmlmypy -p ProjectModel - report: 'htmlmypy' - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} + html_artifact: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }} PublishTestResults: - uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0 + uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@dev needs: - UnitTesting Package: - uses: pyTooling/Actions/.github/workflows/Package.yml@r0 + uses: pyTooling/Actions/.github/workflows/Package.yml@dev needs: - Params - Coverage with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} + python_version: ${{ needs.Params.outputs.python_version }} + artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }} Release: - uses: pyTooling/Actions/.github/workflows/Release.yml@r0 + uses: pyTooling/Actions/.github/workflows/Release.yml@dev if: startsWith(github.ref, 'refs/tags') needs: - UnitTesting @@ -68,48 +66,48 @@ jobs: - Package PublishOnPyPI: - uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0 + uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@dev if: startsWith(github.ref, 'refs/tags') needs: - Params - Release - Package with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} + python_version: ${{ needs.Params.outputs.python_version }} requirements: -r dist/requirements.txt - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} + artifact: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }} secrets: PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} VerifyDocs: - uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r0 + uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@dev needs: - Params with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} + python_version: ${{ needs.Params.outputs.python_version }} BuildTheDocs: - uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0 + uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@dev needs: - Params - VerifyDocs with: - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} + artifact: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }} PublishToGitHubPages: - uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0 + uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@dev needs: - Params - BuildTheDocs - Coverage - StaticTypeCheck with: - doc: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} - coverage: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} - typing: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} + doc: ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }} + coverage: ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }} + typing: ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }} ArtifactCleanUp: - uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0 + uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@dev needs: - Params - UnitTesting @@ -119,21 +117,9 @@ jobs: - PublishToGitHubPages - PublishTestResults with: - package: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} + package: ${{ fromJson(needs.Params.outputs.artifact_names).package_all }} remaining: | - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.7 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.8 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.9 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-ubuntu-3.10 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.7 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.8 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.9 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-windows-3.10 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-msys2-3.9 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.7 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.8 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.9 - ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }}-macos-3.10 - ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} - ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} - ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} + ${{ fromJson(needs.Params.outputs.artifact_names).unittesting_xml }}-* + ${{ fromJson(needs.Params.outputs.artifact_names).codecoverage_html }} + ${{ fromJson(needs.Params.outputs.artifact_names).statictyping_html }} + ${{ fromJson(needs.Params.outputs.artifact_names).documentation_html }} diff --git a/.idea/pyEDAA.ProjectModel.iml b/.idea/pyEDAA.ProjectModel.iml index 419700c1..8d7474b0 100644 --- a/.idea/pyEDAA.ProjectModel.iml +++ b/.idea/pyEDAA.ProjectModel.iml @@ -7,8 +7,10 @@ + + - + \ No newline at end of file diff --git a/dist/requirements.txt b/dist/requirements.txt index 6c4932c1..e4718c94 100644 --- a/dist/requirements.txt +++ b/dist/requirements.txt @@ -1,2 +1,2 @@ -wheel +wheel>=0.38.1 twine diff --git a/doc/Dependency.rst b/doc/Dependency.rst index 3f5fd20a..bcebdeb3 100644 --- a/doc/Dependency.rst +++ b/doc/Dependency.rst @@ -27,9 +27,9 @@ pyEDAA.ProjectModel Package +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Package** | **Version** | **License** | **Dependencies** | +==========================================================+=============+===========================================================================================+========================================================================================================================================================+ -| `pyTooling `__ | ≥1.9.2 | `Apache License, 2.0 `__ | *None* | +| `pyTooling `__ | ≥5.0.0 | `Apache License, 2.0 `__ | *None* | +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `pyVHDLModel `__ | ≥0.14.2 | `Apache License, 2.0 `__ | * `pyTooling `__ (`Apache License, 2.0 `__) | +| `pyVHDLModel `__ | ≥0.27.1 | `Apache License, 2.0 `__ | * `pyTooling `__ (`Apache License, 2.0 `__) | +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | `pySVModel `__ | ≥0.3.1 | `Apache License, 2.0 `__ | * `pyTooling `__ (`Apache License, 2.0 `__) | +----------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -59,15 +59,15 @@ the mandatory dependencies too. +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ | **Package** | **Version** | **License** | **Dependencies** | +===========================================================+=============+========================================================================================+======================+ -| `pytest `__ | ≥6.2.5 | `MIT `__ | *Not yet evaluated.* | +| `pytest `__ | ≥7.2.0 | `MIT `__ | *Not yet evaluated.* | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `pytest-cov `__ | ≥3.0.0 | `MIT `__ | *Not yet evaluated.* | +| `pytest-cov `__ | ≥4.0.0 | `MIT `__ | *Not yet evaluated.* | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `Coverage `__ | ≥6.2 | `Apache License, 2.0 `__ | *Not yet evaluated.* | +| `Coverage `__ | ≥7.0 | `Apache License, 2.0 `__ | *Not yet evaluated.* | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `mypy `__ | ≥0.931 | `MIT `__ | *Not yet evaluated.* | +| `mypy `__ | ≥0.990 | `MIT `__ | *Not yet evaluated.* | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `lxml `__ | ≥4.6.4 | `BSD 3-Clause `__ | *Not yet evaluated.* | +| `lxml `__ | ≥4.9 | `BSD 3-Clause `__ | *Not yet evaluated.* | +-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ @@ -95,15 +95,15 @@ the mandatory dependencies too. +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Package** | **Version** | **License** | **Dependencies** | +=================================================================================================+==============+==========================================================================================================+======================================================================================================================================================+ -| `pyTooling `__ | ≥1.9.2 | `Apache License, 2.0 `__ | *None* | +| `pyTooling `__ | ≥5.0.0 | `Apache License, 2.0 `__ | *None* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Sphinx `__ | ≥4.3.0 | `BSD 3-Clause `__ | *Not yet evaluated.* | +| `Sphinx `__ | ≥5.3.0 | `BSD 3-Clause `__ | *Not yet evaluated.* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | `sphinx_btd_theme `__ | ≥0.5.2 | `MIT `__ | *Not yet evaluated.* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | !! `sphinx_fontawesome `__ | ≥0.0.6 | `GPL 2.0 `__ | *Not yet evaluated.* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `sphinx_autodoc_typehints `__ | ≥1.14.1 | `MIT `__ | *Not yet evaluated.* | +| `sphinx_autodoc_typehints `__ | ≥1.19.5 | `MIT `__ | *Not yet evaluated.* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -131,9 +131,9 @@ install the mandatory dependencies too. +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Package** | **Version** | **License** | **Dependencies** | +============================================================================+==============+==========================================================================================================+======================================================================================================================================================+ -| `pyTooling `__ | ≥1.9.2 | `Apache License, 2.0 `__ | *None* | +| `pyTooling `__ | ≥5.0.0 | `Apache License, 2.0 `__ | *None* | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `wheel `__ | any | `MIT `__ | *Not yet evaluated.* | +| `wheel `__ | ≥0.38.1 | `MIT `__ | *Not yet evaluated.* | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -162,7 +162,7 @@ install the mandatory dependencies too. +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ | **Package** | **Version** | **License** | **Dependencies** | +==========================================================+==============+===========================================================================================+======================+ -| `wheel `__ | any | `MIT `__ | *Not yet evaluated.* | +| `wheel `__ | ≥0.38.1 | `MIT `__ | *Not yet evaluated.* | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ | `Twine `__ | any | `Apache License, 2.0 `__ | *Not yet evaluated.* | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ diff --git a/doc/_extensions/DocumentMember.py b/doc/_extensions/DocumentMember.py deleted file mode 100644 index 516832e8..00000000 --- a/doc/_extensions/DocumentMember.py +++ /dev/null @@ -1,51 +0,0 @@ -# ============================================================================== -# Authors: Patrick Lehmann -# -# Python Module: -# -# Description: -# ------------------------------------ -# - TODO -# -# License: -# ============================================================================== -# Copyright 2007-2016 Patrick Lehmann - Dresden, Germany -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============================================================================== -# -from SphinxExtensions import DocumentMemberAttribute - - -def skip_member_handler(app, what, name, obj, skip, options): - # try: - # print("skip_member_handler: ", obj) - # except: - # print("skip_member_handler: ERROR") - - try: - attributes = DocumentMemberAttribute.GetAttributes(obj) - if (len(attributes) > 0): - # print("*#"*20) - # try: - # print("skip_member_handler: ", obj) - # except: - # print("skip_member_handler: ERROR") - - return not attributes[0].value - except: - pass - return None - -def setup(app): - app.connect('autodoc-skip-member', skip_member_handler) diff --git a/doc/_extensions/autoapi/__init__.py b/doc/_extensions/autoapi/__init__.py deleted file mode 100644 index 52a5a3f9..00000000 --- a/doc/_extensions/autoapi/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 Carlos Jenkins -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -from __future__ import unicode_literals, absolute_import -from __future__ import print_function, division - -from .apinode import __doc__, APINode # noqa - -__author__ = 'Carlos Jenkins' -__email__ = 'carlos@jenkins.co.cr' -__version__ = '1.3.1' - -__all__ = ['APINode'] diff --git a/doc/_extensions/autoapi/apinode.py b/doc/_extensions/autoapi/apinode.py deleted file mode 100644 index 9fbee1af..00000000 --- a/doc/_extensions/autoapi/apinode.py +++ /dev/null @@ -1,353 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 Carlos Jenkins -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -""" -Module that provides the module tree node :class:`APINode`. - -This class will load the module identified by ``name`` and recursively build a -tree with all it's submodules and subpackages. In the process, each node -analyze and fetch the public API of that module. - -``name`` can be any node, like the root package, or any subpackage or submodule -and a tree will be built from there. ``name`` must follow the standard -"dot notation" for importing a module. - -This class will not assume any special naming, or perform any complex analysis -to determine what must be in the public interface. This is because it is not -only a difficult problem, but it involves analyzing deeply the namespace of the -module which can be quite expensive. - -In general it is very difficult to determine in a module namespace what -elements are private or public declared locally, private or public but declared -in another module and brought into the module local namespace -(``from x import y``), third party library, Python standard library, etc. At -the end, any algorithm that tries to determine this will eventually fail to -meet the requirements or expectations of the developer, leaving false positives -or removing elements expected to be present in the public API. - -For example, a common scenario is that some modules, specially package entry -points ``__init__.py``, can be setup to expose the public API of their sibling -modules, possible causing several objects to be identified as part of the -public API of both modules. - -Because of this the approach taken by this module follows the rule in PEP20 -"Explicit is better than implicit". In consequence, the node will consider -elements as public if they are explicitly listed in the ``__api__`` or -``__all__`` variables. It is up to the developer to list the elements that must -be published in the public API. - -``__api__`` is a special variable introduced by this module, and it exists for -situation were for whatever reason the developer don't want to list in the -``__all__`` variable an element that needs to be published in the public API. - -This class will extract all elements identified in ONE of those listings (not -the union), with ``__api__`` having the precedence. If none of those variables -exists in the module then it will be assumed that no public API exists for that -module and no futher actions will be taken. - -If any of those variables exists this class will iterate all elements listed in -them and will catalog them in four categories: - -- Functions. -- Exceptions. -- Classes. -- Variables. - -Being Variables the default if it cannot be determined that an element belongs -to any of other categories. -""" - -from __future__ import unicode_literals, absolute_import -from __future__ import print_function, division - -from logging import getLogger -from traceback import format_exc -from importlib import import_module -from pkgutil import iter_modules -from inspect import isclass, isfunction -from collections import OrderedDict - - -log = getLogger(__name__) - - -class APINode(object): - """ - Tree node class for module instrospection. - - :param str name: Name of the module to build the tree from. It must follow - the "dot notation" of the import mechanism. - :param dict directory: Directory to store the index of all the modules. - If None, the default, the root node will create one a pass it to the - subnodes. - - **Attributes:** - - :var name: Name of the current module. - :var subname: Last part of the name of this module. For example if name is - ``my.module.another`` the subname will be ``another``. - :var directory: Directory of the tree. This is a :py:class:`OrderedDict` - that will register all modules name with it's associated node - :class:`APINode`. All nodes of a tree share this index and thus - the whole tree can be queried from any node. - :var module: The loaded module. - :var subnodes: A list of :class:`APINode` with all child submodules - and subpackages. - :var subnodes_failed: A list of submodules and subpackages names that - failed to import. - - **Public API categories:** - - :var functions: A :py:class:`OrderedDict` of all functions found in the - public API of the module. - :var classes: A :py:class:`OrderedDict` of all classes found in the - public API of the module. - :var exceptions: A :py:class:`OrderedDict` of all exceptions found in the - public API of the module. - :var variables: A :py:class:`OrderedDict` of all other elements found in - the public API of the module. - - In all categories the order on which the elements are listed is preserved. - """ - - def __init__(self, name, directory=None): - self.module = import_module(name) - self.name = name - self.subname = name.split('.')[-1] - - self.functions = OrderedDict() - self.classes = OrderedDict() - self.exceptions = OrderedDict() - self.variables = OrderedDict() - self.api = OrderedDict(( - ('functions', self.functions), - ('classes', self.classes), - ('exceptions', self.exceptions), - ('variables', self.variables), - )) - - self.subnodes = [] - self.subnodes_failed = [] - - self.directory = OrderedDict() - if directory is not None: - self.directory = directory - - self._relevant = None - - # Now that all node public attributes exists and module was imported - # register itself in the directory - self.directory[self.name] = self - - # Check if package and iterate over subnodes - if hasattr(self.module, '__path__'): - for _, subname, ispkg in iter_modules( - self.module.__path__, self.module.__name__ + '.'): - log.info('Recursing into {}'.format(subname)) - - try: - subnode = APINode(subname, self.directory) - self.subnodes.append(subnode) - except: # Overbroad exception handling on purpose - log.error('Failed to import {}'.format(subname)) - log.debug(format_exc()) - self.subnodes_failed.append(subname) - - # Fetch all public objects - public = OrderedDict() - for public_key in ['__api__', '__all__']: - if not hasattr(self.module, public_key): - continue - - for obj_name in getattr(self.module, public_key): - if not hasattr(self.module, obj_name): - log.warning( - 'Module {} doesn\'t have a element {}'.format( - self.name, obj_name - ) - ) - continue - public[obj_name] = getattr(self.module, obj_name) - break - - # Categorize objects - for obj_name, obj in public.items(): - if isclass(obj): - if issubclass(obj, Exception): - self.exceptions[obj_name] = obj - continue - self.classes[obj_name] = obj - continue - if isfunction(obj): - self.functions[obj_name] = obj - continue - self.variables[obj_name] = obj - - # Flag to mark if this branch is relevant - # For self._relevant, None means undertermined - if self.is_root(): - self.is_relevant() - - def has_public_api(self): - """ - Check if this node has a public API. - - :rtype: bool - :return: True if any category has at least one element. - """ - return any(self.api.values()) - - def is_leaf(self): - """ - Check if the current node is a leaf in the tree. - - A leaf node not necessarily is a module, it can be a package without - modules (just the entry point ``__init__.py``). - - :rtype: bool - :return: True if no other subnodes exists for this node. - """ - return not self.subnodes - - def is_root(self): - """ - Check if the current node is the root node. - - :rtype: bool - :return: True if the current node is the root node. - """ - for key in self.directory.keys(): - return key == self.name - raise Exception('Empty directory!') - - def is_relevant(self): - """ - Check if this branch of the tree is relevant. - - A branch is relevant if the current node has a public API or if any of - its subnodes is relevant (in order to reach relevant nodes). - - Relevancy is determined at initialization by the root node. - - :rtype: bool - :return: True if the current node is relevant. - """ - if self._relevant is not None: - return self._relevant - - relevant = False - if self.has_public_api() or \ - any(s.is_relevant() for s in self.subnodes): - relevant = True - - self._relevant = relevant - - return self._relevant - - def depth(self): - """ - Get the depth of the current node in the tree. - - :rtype: int - :return: The depth of the node. For example, for node ``my.add.foo`` - the depth is 3. - """ - return len(self.name.split('.')) - - def get_module(self, name): - """ - Get a module node by it's name. - - This is just a helper that does lookup on the directory index. - - :rtype: :class:`APINode` or None - :return: The module node identified by ``name`` in the tree. ``None`` - if the name doesn't exists. - """ - return self.directory.get(name, None) - - def walk(self): - """ - Traverse the tree top-down. - - :return: This method will yield tuples ``(node, [leaves])`` for each - node in the tree. - """ - if self.is_leaf(): - raise StopIteration() - - yield (self, [n for n in self.subnodes if n.is_leaf()]) - - for subnode in [n for n in self.subnodes if not n.is_leaf()]: - for step in subnode.walk(): - yield step - - def __iter__(self): - return self.walk - - def tree(self, level=0, fullname=True): - """ - Pretty print the subtree at the current node. - - For example, for the module ``confspec``: - - :: - - confspec - confspec.manager [c] - confspec.options [c] - confspec.providers [c, v] - confspec.providers.dict [c] - confspec.providers.ini [c] - confspec.providers.json [c] - confspec.utils [f] - confspec.validation [f] - - The tags at the right of the name shows what kind of elements are - present in the public interfaces of those modules. - - :param int level: Indentation level. - :param bool fullname: Plot the full name of the module or just it's - subname. - """ - name = [(' ' * level)] - if fullname: - name.append(self.name) - else: - name.append(self.subname) - - tags = [] - for tag, category in zip(['f', 'c', 'e', 'v'], self.api.values()): - if category: - tags.append(tag) - if tags: - name.append(' [{}]'.format(', '.join(tags))) - - output = [''.join(name)] - for subnode in self.subnodes: - output.append(subnode.tree(level=level + 1, fullname=fullname)) - return '\n'.join(output) - - def __str__(self): - return self.tree() - - def __repr__(self): - return self.name - - -__all__ = ['APINode'] -__api__ = [] diff --git a/doc/_extensions/autoapi/sphinx.py b/doc/_extensions/autoapi/sphinx.py deleted file mode 100644 index e7ec50d0..00000000 --- a/doc/_extensions/autoapi/sphinx.py +++ /dev/null @@ -1,209 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (C) 2015 Carlos Jenkins -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -""" -Glue for Sphinx API. -""" - -from __future__ import unicode_literals, absolute_import -from __future__ import print_function, division - -from inspect import getdoc -from logging import getLogger -from traceback import format_exc -from os.path import join, dirname, abspath, exists -from os import environ - -from jinja2.sandbox import SandboxedEnvironment -from sphinx.util.osutil import ensuredir -from sphinx.jinja2glue import BuiltinTemplateLoader - -from . import __version__ -from .apinode import APINode - - -log = getLogger(__name__) - - -def handle_exception(func): - """ - Utility decorator to report all exceptions in module without making Sphinx - to die. - """ - def wrapper(app): - try: - func(app) - except Exception: - app.warn( - 'Unhandled exception in autoapi module: \n{}'.format( - format_exc() - ) - ) - - # Preserve docstring - if hasattr(func, '__doc__'): - wrapper.__doc__ = func.__doc__ - - return wrapper - - -def filter_summary(obj): - """ - Jinja2 filter that allows to extract the documentation summary of an - object. - """ - try: - doc = getdoc(obj) - if doc is None: - return 'Undocumented.' - - summary = doc.split('\n').pop(0) - summary.replace('\\', '\\\\') # Escape backslash in RST - return summary - except: - log.error( - 'AutoApi failed to determine autosummary for obj: {}'.format(obj) - ) - log.error(format_exc()) - - return 'AutoApi: Unable to determine summary.' - - -def get_template_env(app): - """ - Get the template environment. - - .. note:: - - Template should be loaded as a package_data using - :py:function:`pkgutil.get_data`, but because we want the user to - override the default template we need to hook it to the Sphinx loader, - and thus a file system approach is required as it is implemented like - that. - """ - template_dir = [join(dirname(abspath(__file__)), 'templates')] - template_loader = BuiltinTemplateLoader() - template_loader.init(app.builder, dirs=template_dir) - template_env = SandboxedEnvironment(loader=template_loader) - template_env.filters['summary'] = filter_summary - return template_env - - -@handle_exception -def builder_inited(app): - """ - autoapi Sphinx extension hook for the ``builder-inited`` event. - - This hook will read the configuration value ``autoapi_modules`` and render - the modules described in it. - - See http://sphinx-doc.org/extdev/appapi.html#event-builder-inited - """ - # Get modules to build documentation for - modules = app.config.autoapi_modules - if not modules: - return - - # Overwrite all files in an ReadTheDocs environment when the first builder runs (pickle) - overrideDefault = True - if (environ.get('READTHEDOCS') == "True"): - #if (app.buildername != "pickle"): - overrideDefault = False - - # Get template environment - template_env = get_template_env(app) - - print("===============================") - print("overrideDefault set to: {0!s}".format(overrideDefault)) - print("===============================") - - for module, overrides in modules.items(): - - # Get options - options = { - 'prune': False, - 'override': overrideDefault, - 'template': 'module', - 'output': module - } - if overrides: - options.update(overrides) - - # Get template - template = template_env.get_template( - 'autoapi/{}.rst'.format(options['template']) - ) - - # Build API tree - tree = APINode(module) - - # Gather nodes to document - if options['prune']: - nodes = [ - node for node in tree.directory.values() - if node.is_relevant() - ] - else: - nodes = tree.directory.values() - - if not nodes: - continue - - # Define output directory - out_dir = join(app.env.srcdir, options['output']) - ensuredir(out_dir) - - # Iterate nodes and render them - for node in nodes: - out_file = join(out_dir, node.name + app.config.source_suffix[0]) - - # Skip file if it override is off and it exists - if not options['override'] and exists(out_file): - continue - - # Consider only subnodes that are relevant if prune is enabled - subnodes = node.subnodes - if options['prune']: - subnodes = [ - subnode for subnode in node.subnodes - if subnode.is_relevant() - ] - - # Write file - with open(out_file, 'w') as fd: - fd.write( - template.render( - node=node, - subnodes=subnodes - ) - ) - - -def setup(app): - """ - autoapi Sphinx extension setup. - - See http://sphinx-doc.org/extdev/tutorial.html#the-setup-function - """ - # autodoc is required - app.setup_extension('sphinx.ext.autodoc') - app.add_config_value('autoapi_modules', {}, True) - app.connect(str('builder-inited'), builder_inited) - return {'version': __version__} - - -__all__ = ['builder_inited', 'setup'] diff --git a/doc/_templates/autoapi/module.rst b/doc/_templates/autoapi/module.rst index 8ded38ea..655beff4 100644 --- a/doc/_templates/autoapi/module.rst +++ b/doc/_templates/autoapi/module.rst @@ -1,16 +1,20 @@ -{{ node.name }} +.. # Template modified by Patrick Lehmann + * removed automodule on top, because private members are activated for autodoc (no doubled documentation). + * Made sections like 'submodules' bold text, but no headlines to reduce number of ToC levels. + +=={{ '=' * node.name|length }}== +``{{ node.name }}`` =={{ '=' * node.name|length }}== -.. automodule:: {{ node.name }} +.. py:module:: {{ node.name }} {##} {%- block modules -%} {%- if subnodes %} -.. #----------------------------------- -{##} **Submodules** + .. toctree:: {% for item in subnodes %} {{ item.name }} @@ -21,123 +25,85 @@ {##} .. currentmodule:: {{ node.name }} {##} +{%- block functions -%} +{%- if node.functions %} -.. #----------------------------------- -{##} -{%- if node.variables %} -**Variables** -{##} -{% for item, obj in node.variables.items() -%} -- :py:data:`{{ item }}` -{% endfor -%} -{%- endif -%} - +**Functions** -{%- if node.exceptions %} -{##} -**Exceptions** -{##} -{% for item, obj in node.exceptions.items() -%} -- :py:exc:`{{ item }}`: +{% for item, obj in node.functions.items() -%} +- :py:func:`{{ item }}`: {{ obj|summary }} {% endfor -%} -{%- endif -%} +{% for item in node.functions %} +.. autofunction:: {{ item }} +{##} +{%- endfor -%} +{%- endif -%} +{%- endblock -%} +{%- block classes -%} {%- if node.classes %} -{##} + **Classes** -{##} + {% for item, obj in node.classes.items() -%} - :py:class:`{{ item }}`: {{ obj|summary }} {% endfor -%} -{%- endif -%} - - -{%- if node.functions %} -{##} -**Functions** -{##} -{% for item, obj in node.functions.items() -%} -- :py:func:`{{ item }}`: - {{ obj|summary }} - -{% endfor -%} -{%- endif -%} - - -{%- block variables -%} -{%- if node.variables %} -{% for item, obj in node.variables.items() %} -.. autodata:: {{ item }} - :annotation: - .. code-block:: guess +{% for item in node.classes %} +.. autoclass:: {{ item }} + :members: - {{ obj|pprint|indent(6) }} + .. rubric:: Inheritance + .. inheritance-diagram:: {{ item }} + :parts: 1 {##} {%- endfor -%} {%- endif -%} {%- endblock -%} - {%- block exceptions -%} {%- if node.exceptions %} -.. #----------------------------------- - -{% for item in node.exceptions %} -.. autoexception:: {{ item }} - :members: - :private-members: - :inherited-members: - :undoc-members: -{##} - .. rubric:: Inheritance - .. inheritance-diagram:: {{ item }} -{##} - .. rubric:: Members -{##} -{%- endfor -%} -{%- endif -%} -{%- endblock -%} +**Exceptions** +{% for item, obj in node.exceptions.items() -%} +- :py:exc:`{{ item }}`: + {{ obj|summary }} -{%- block classes -%} -{%- if node.classes %} +{% endfor -%} -.. #----------------------------------- +{% for item in node.exceptions %} +.. autoexception:: {{ item }} -{% for item in node.classes %} -.. autoclass:: {{ item }} - :members: - :private-members: - :undoc-members: - :inherited-members: -{##} .. rubric:: Inheritance .. inheritance-diagram:: {{ item }} :parts: 1 -{##} - .. rubric:: Members {##} {%- endfor -%} {%- endif -%} {%- endblock -%} +{%- block variables -%} +{%- if node.variables %} -{%- block functions -%} -{%- if node.functions %} +**Variables** -.. #----------------------------------- +{% for item, obj in node.variables.items() -%} +- :py:data:`{{ item }}` +{% endfor -%} -**Functions** +{% for item, obj in node.variables.items() %} +.. autodata:: {{ item }} + :annotation: -{% for item in node.functions %} -.. autofunction:: {{ item }} + .. code-block:: text + + {{ obj|pprint|indent(6) }} {##} {%- endfor -%} {%- endif -%} diff --git a/doc/_templates/autoapi/script.rst b/doc/_templates/autoapi/script.rst deleted file mode 100644 index ab52306a..00000000 --- a/doc/_templates/autoapi/script.rst +++ /dev/null @@ -1,149 +0,0 @@ -{{ node.name }}.py -=={{ '=' * node.name|length }}== - -.. automodule:: {{ node.name }} - - -{##} -{%- block modules -%} -{%- if subnodes %} - -.. #----------------------------------- -{##} -**Submodules** - -.. toctree:: -{% for item in subnodes %} - {{ item.name }} -{%- endfor %} -{##} -{%- endif -%} -{%- endblock -%} -{##} -.. currentmodule:: {{ node.name }} -{##} - -.. #----------------------------------- -{##} -{%- if node.variables %} -**Variables** -{##} -{% for item, obj in node.variables.items() -%} -- :py:data:`{{ item }}` -{% endfor -%} -{%- endif -%} - - -{%- if node.exceptions %} -{##} -**Exceptions** -{##} -{% for item, obj in node.exceptions.items() -%} -- :py:exc:`{{ item }}`: - {{ obj|summary }} - -{% endfor -%} -{%- endif -%} - - -{%- if node.classes %} -{##} -**Classes** -{##} -{% for item, obj in node.classes.items() -%} -- :py:class:`{{ item }}`: - {{ obj|summary }} - -{% endfor -%} -{%- endif -%} - - -{%- if node.functions %} -{##} -**Functions** -{##} -{% for item, obj in node.functions.items() -%} -- :py:func:`{{ item }}`: - {{ obj|summary }} - -{% endfor -%} -{%- endif -%} - - -{%- block variables -%} -{%- if node.variables %} -{% for item, obj in node.variables.items() %} -.. autodata:: {{ item }} - :noindex: - :annotation: - - .. code-block:: guess - - {{ obj|pprint|indent(6) }} -{##} -{%- endfor -%} -{%- endif -%} -{%- endblock -%} - - -{%- block exceptions -%} -{%- if node.exceptions %} - -.. #----------------------------------- - -{% for item in node.exceptions %} -.. autoexception:: {{ item }} - :members: - :noindex: - :private-members: - :inherited-members: - :undoc-members: -{##} - .. rubric:: Inheritance - .. inheritance-diagram:: {{ item }} -{##} - .. rubric:: Members -{##} -{%- endfor -%} -{%- endif -%} -{%- endblock -%} - - -{%- block classes -%} -{%- if node.classes %} - -.. #----------------------------------- - -{% for item in node.classes %} -.. autoclass:: {{ item }} - :members: - :noindex: - :private-members: - :undoc-members: - :inherited-members: -{##} - .. rubric:: Inheritance - .. inheritance-diagram:: {{ item }} - :parts: 1 -{##} - .. rubric:: Members -{##} -{%- endfor -%} -{%- endif -%} -{%- endblock -%} - - -{%- block functions -%} -{%- if node.functions %} - -.. #----------------------------------- - -**Functions** - -{% for item in node.functions %} -.. autofunction:: {{ item }} - :noindex: -{##} -{%- endfor -%} -{%- endif -%} -{%- endblock -%} diff --git a/doc/conf.py b/doc/conf.py index 4ff89d87..ed1779b7 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -14,7 +14,6 @@ sys_path.insert(0, abspath('.')) sys_path.insert(0, abspath('..')) sys_path.insert(0, abspath('../pyEDAA/ProjectModel')) -#sys_path.insert(0, abspath('_extensions')) # ============================================================================== @@ -33,6 +32,7 @@ version = ".".join(versionInformation.Version.split(".")[:2]) # e.g. 2.3 The short X.Y version. release = versionInformation.Version + # ============================================================================== # Miscellaneous settings # ============================================================================== @@ -59,12 +59,12 @@ # ============================================================================== # Restructured Text settings # ============================================================================== -prologPath = "prolog.inc" +prologPath = Path("prolog.inc") try: - with open(prologPath, "r") as prologFile: - rst_prolog = prologFile.read() + with prologPath.open("r") as fileHandle: + rst_prolog = fileHandle.read() except Exception as ex: - print("[ERROR:] While reading '{0!s}'.".format(prologPath)) + print(f"[ERROR:] While reading '{prologPath}'.") print(ex) rst_prolog = "" @@ -153,7 +153,6 @@ ] - # ============================================================================== # Extensions # ============================================================================== @@ -168,33 +167,15 @@ 'sphinx.ext.mathjax', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', -# 'sphinx.ext.duration', - # SphinxContrib extensions -# 'sphinxcontrib.actdiag', 'sphinxcontrib.mermaid', -# 'sphinxcontrib.seqdiag', -# 'sphinxcontrib.textstyle', -# 'sphinxcontrib.spelling', -# 'changelog', - -# BuildTheDocs extensions -# 'btd.sphinx.autoprogram', -# 'btd.sphinx.graphviz', -# 'btd.sphinx.inheritance_diagram', - # Other extensions -# 'DocumentMember', 'sphinx_fontawesome', 'sphinx_autodoc_typehints', - -# local extensions (patched) 'autoapi.sphinx', - -# local extensions -# 'DocumentMember' ] + # ============================================================================== # Sphinx.Ext.InterSphinx # ============================================================================== @@ -207,7 +188,16 @@ # Sphinx.Ext.AutoDoc # ============================================================================== # see: https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#configuration +autodoc_default_options = { + "private-members": True, + "special-members": True, + "inherited-members": True, + "exclude-members": "__weakref__" +} +#autodoc_class_signature = "separated" autodoc_member_order = "bysource" # alphabetical, groupwise, bysource +autodoc_typehints = "both" +#autoclass_content = "both" # ============================================================================== @@ -216,7 +206,7 @@ extlinks = { "ghissue": ('https://GitHub.com/edaa-org/pyEDAA.ProjectModel/issues/%s', 'issue #'), "ghpull": ('https://GitHub.com/edaa-org/pyEDAA.ProjectModel/pull/%s', 'pull request #'), - "ghsrc": ('https://GitHub.com/edaa-org/pyEDAA.ProjectModel/blob/main/%s?ts=2', None), + "ghsrc": ('https://GitHub.com/edaa-org/pyEDAA.ProjectModel/blob/main/%s', ''), } diff --git a/doc/prolog.inc b/doc/prolog.inc index a3e09f25..75463a87 100644 --- a/doc/prolog.inc +++ b/doc/prolog.inc @@ -20,7 +20,12 @@ .. role:: bolditalic @@ -29,5 +34,26 @@ .. role:: underline :class: underline +.. role:: strike + :class: strike + .. role:: xlarge :class: xlarge + +.. role:: red + :class: colorred +.. role:: green + :class: colorgreen +.. role:: blue + :class: colorblue +.. role:: purple + :class: colorpurple + +.. role:: deletion + :class: colorred strike +.. role:: addition + :class: colorgreen + +.. role:: pycode(code) + :language: python + :class: highlight diff --git a/doc/pyEDAA.ProjectModel/index.rst b/doc/pyEDAA.ProjectModel/index.rst index 26e2e882..118a264a 100644 --- a/doc/pyEDAA.ProjectModel/index.rst +++ b/doc/pyEDAA.ProjectModel/index.rst @@ -1,3 +1,8 @@ +Python Class Reference +###################### + +Reference of all packages and modules: + .. toctree:: pyEDAA.ProjectModel diff --git a/doc/requirements.txt b/doc/requirements.txt index 48fdad5f..8c659351 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,12 +1,12 @@ -r ../requirements.txt -pyTooling>=1.9.2 +pyTooling >= 5.0.0 # Enforce latest version on ReadTheDocs -sphinx>=4.3.0 +sphinx>=5.3.0 # Sphinx Extenstions sphinxcontrib-mermaid>=0.7.1 autoapi>=2.0.1 sphinx_fontawesome>=0.0.6 -sphinx_autodoc_typehints>=1.14.1 +sphinx_autodoc_typehints>=1.19.5 diff --git a/tests/project/lib/file1.vhdl b/pyEDAA/ProjectModel/VHDL.py similarity index 100% rename from tests/project/lib/file1.vhdl rename to pyEDAA/ProjectModel/VHDL.py diff --git a/pyEDAA/ProjectModel/Xilinx/Vivado.py b/pyEDAA/ProjectModel/Xilinx/Vivado.py index 3370bade..02e1cd96 100644 --- a/pyEDAA/ProjectModel/Xilinx/Vivado.py +++ b/pyEDAA/ProjectModel/Xilinx/Vivado.py @@ -33,6 +33,8 @@ from typing import Iterable from xml.dom import minidom, Node + +from pyTooling.MetaClasses import ExtendedType from pyVHDLModel import VHDLVersion from pyTooling.Decorators import export @@ -54,7 +56,7 @@ class File(Model_File): pass -class VivadoFileMixIn: +class VivadoFileMixIn(metaclass=ExtendedType, mixin=True): def _registerAttributes(self): self._attributes[UsedInAttribute] = [] diff --git a/pyEDAA/ProjectModel/__init__.py b/pyEDAA/ProjectModel/__init__.py index 1e3b982d..bb46b619 100644 --- a/pyEDAA/ProjectModel/__init__.py +++ b/pyEDAA/ProjectModel/__init__.py @@ -34,16 +34,18 @@ __email__ = "Paebbels@gmail.com" __copyright__ = "2014-2022, Patrick Lehmann, Unai Martinez-Corral" __license__ = "Apache License, Version 2.0" -__version__ = "0.4.2" +__version__ = "0.4.3" __keywords__ = ["eda project", "model", "abstract", "xilinx", "vivado", "osvvm", "file set", "file group", "test bench", "test harness"] from os.path import relpath as path_relpath from pathlib import Path as pathlib_Path from typing import Dict, Union, Optional as Nullable, List, Iterable, Generator, Tuple, Any as typing_Any, Type -from pyTooling.Decorators import export -from pySVModel import VerilogVersion, SystemVerilogVersion -from pyVHDLModel import VHDLVersion +from pyTooling.Decorators import export +from pyTooling.MetaClasses import ExtendedType +from pyTooling.Graph import Graph, Vertex +from pySVModel import VerilogVersion, SystemVerilogVersion +from pyVHDLModel import VHDLVersion @export @@ -64,7 +66,7 @@ def resolve(obj: typing_Any, key: Type['Attribute']): @export -class FileType(type): +class FileType(ExtendedType): """ A :term:`meta-class` to construct *FileType* classes. @@ -79,8 +81,8 @@ def __init__(cls, name: str, bases: Tuple[type, ...], dictionary: Dict[str, typi super().__init__(name, bases, dictionary, **kwargs) cls.Any = cls - def __new__(cls, className, baseClasses, classMembers: Dict): - fileType = super().__new__(cls, className, baseClasses, classMembers) + def __new__(cls, className, baseClasses, classMembers: Dict, *args, **kwargs): + fileType = super().__new__(cls, className, baseClasses, classMembers, *args, **kwargs) cls.FileTypes[className] = fileType return fileType @@ -95,7 +97,7 @@ def __contains__(cls, item) -> bool: @export -class File(metaclass=FileType): +class File(metaclass=FileType, slots=True): """ A :term:`File` represents a file in a design. This :term:`base-class` is used for all derived file classes. @@ -264,7 +266,7 @@ def __setitem__(self, key: Type[Attribute], value: typing_Any): @export -class HumanReadableContent: +class HumanReadableContent(metaclass=ExtendedType, mixin=True): """A file type representing human-readable contents.""" @@ -367,8 +369,27 @@ class VHDLSourceFile(HDLSourceFile, HumanReadableContent): def __init__(self, path: pathlib_Path, vhdlLibrary: Union[str, 'VHDLLibrary'] = None, vhdlVersion: VHDLVersion = None, project: 'Project' = None, design: 'Design' = None, fileSet: 'FileSet' = None): super().__init__(path, project, design, fileSet) - # TODO: handle if vhdlLibrary is a string - self._vhdlLibrary = vhdlLibrary + if isinstance(vhdlLibrary, str): + if design is not None: + try: + vhdlLibrary = design.VHDLLibraries[vhdlLibrary] + except KeyError as ex: + raise Exception(f"VHDL library '{vhdlLibrary}' not found in design '{design.Name}'.") from ex + elif project is not None: + try: + vhdlLibrary = project.DefaultDesign.VHDLLibraries[vhdlLibrary] + except KeyError as ex: + raise Exception(f"VHDL library '{vhdlLibrary}' not found in default design '{project.DefaultDesign.Name}'.") from ex + else: + raise Exception(f"Can't lookup VHDL library because neither 'project' nor 'design' is given as a parameter.") + elif isinstance(vhdlLibrary, VHDLLibrary): + self._vhdlLibrary = vhdlLibrary + vhdlLibrary.AddFile(self) + elif vhdlLibrary is None: + self._vhdlLibrary = None + else: + raise TypeError(f"Parameter 'vhdlLibrary' is neither a 'str' nor 'VHDLibrary'.") + self._vhdlVersion = vhdlVersion def Validate(self): @@ -413,6 +434,9 @@ def VHDLVersion(self) -> VHDLVersion: def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value + def __repr__(self) -> str: + return f"" + @export class VerilogSourceFile(HDLSourceFile, HumanReadableContent): @@ -538,7 +562,7 @@ class WaveformExchangeFile(File): @export -class FileSet: +class FileSet(metaclass=ExtendedType, slots=True): """ A :term:`FileSet` represents a group of files. Filesets can have sub-filesets. @@ -591,7 +615,8 @@ def __init__( self._topLevel = topLevel if project is not None: self._project = project - self._design = design + self._design = design if design is not None else project.DefaultDesign + elif design is not None: self._project = design._project self._design = design @@ -735,7 +760,7 @@ def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[bool, str, 'F try: fileSet = self._fileSets[fileSetName] except KeyError as ex: - raise Exception("Fileset {name} not bound to fileset {fileset}.".format(name=fileSetName, fileset=self.Name)) from ex + raise Exception(f"Fileset {fileSetName} not bound to fileset {self.Name}.") from ex elif not isinstance(fileSet, FileSet): raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.") @@ -812,6 +837,10 @@ def __setitem__(self, key: Type[Attribute], value: typing_Any): def GetOrCreateVHDLLibrary(self, name): if name in self._vhdlLibraries: return self._vhdlLibraries[name] + elif name in self._design._vhdlLibraries: + library = self._design._vhdlLibraries[name] + self._vhdlLibraries[name] = library + return library else: library = VHDLLibrary(name, design=self._design, vhdlVersion=self._vhdlVersion) self._vhdlLibraries[name] = library @@ -887,7 +916,7 @@ def __str__(self): @export -class VHDLLibrary: +class VHDLLibrary(metaclass=ExtendedType, slots=True): """ A :term:`VHDLLibrary` represents a group of VHDL source files compiled into the same VHDL library. @@ -903,6 +932,8 @@ class VHDLLibrary: _files: List[File] _vhdlVersion: VHDLVersion + _dependencyNode: Vertex + def __init__( self, name: str, @@ -913,13 +944,29 @@ def __init__( self._name = name if project is not None: self._project = project - self._design = design + self._design = project._defaultDesign if design is None else design + self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph) + + if name in self._design._vhdlLibraries: + raise Exception(f"Library '{name}' already in design '{self._design.Name}'.") + else: + self._design._vhdlLibraries[name] = self + elif design is not None: self._project = design._project - self._design = design + self._design = design + self._dependencyNode = Vertex(value=self, graph=design._vhdlLibraryDependencyGraph) + + if name in design._vhdlLibraries: + raise Exception(f"Library '{name}' already in design '{design.Name}'.") + else: + design._vhdlLibraries[name] = self + else: self._project = None self._design = None + self._dependencyNode = None + self._files = [] self._vhdlVersion = vhdlVersion @@ -934,7 +981,16 @@ def Project(self) -> Nullable['Project']: @Project.setter def Project(self, value: 'Project'): - self._project = value + if not isinstance(value, Project): + raise TypeError("Parameter 'value' is not of type 'Project'.") + + if value is None: + # TODO: unlink VHDLLibrary from project + self._project = None + else: + self._project = value + if self._design is None: + self._design = value._defaultDesign @property def Design(self) -> Nullable['Design']: @@ -944,13 +1000,26 @@ def Design(self) -> Nullable['Design']: @Design.setter def Design(self, value: 'Design'): if not isinstance(value, Design): - raise TypeError("Parameter 'value' is not of type 'DesignModel.Design'.") + raise TypeError("Parameter 'value' is not of type 'Design'.") - self._design = value - if self._project is None: - self._project = value._project - elif self._project is not value._project: - raise Exception("The design's project is not identical to the already assigned project.") + if value is None: + # TODO: unlink VHDLLibrary from design + self._design = None + else: + if self._design is None: + self._design = value + self._dependencyNode = Vertex(value=self, graph=self._design._vhdlLibraryDependencyGraph) + elif self._design is not value: + # TODO: move VHDLLibrary to other design + # TODO: create new vertex in dependency graph and remove vertex from old graph + self._design = value + else: + pass + + if self._project is None: + self._project = value._project + elif self._project is not value._project: + raise Exception("The design's project is not identical to the already assigned project.") @property def Files(self) -> Generator[File, None, None]: @@ -972,13 +1041,29 @@ def VHDLVersion(self) -> VHDLVersion: def VHDLVersion(self, value: VHDLVersion) -> None: self._vhdlVersion = value + def AddDependency(self, library: 'VHDLLibrary'): + library.parent = self + + def AddFile(self, vhdlFile: VHDLSourceFile) -> None: + if not isinstance(vhdlFile, VHDLSourceFile): + raise TypeError(f"Parameter 'vhdlFile' is not a 'VHDLSourceFile'.") + + self._files.append(vhdlFile) + + def AddFiles(self, vhdlFiles: Iterable[VHDLSourceFile]) -> None: + for vhdlFile in vhdlFiles: + if not isinstance(vhdlFile, VHDLSourceFile): + raise TypeError(f"Item '{vhdlFile}' in parameter 'vhdlFiles' is not a 'VHDLSourceFile'.") + + self._files.append(vhdlFile) + def __str__(self): """Returns the VHDL library's name.""" return self._name @export -class Design: +class Design(metaclass=ExtendedType, slots=True): """ A :term:`Design` represents a group of filesets and the source files therein. @@ -1009,6 +1094,9 @@ class Design: _svVersion: SystemVerilogVersion _externalVHDLLibraries: List + _vhdlLibraryDependencyGraph: Graph + _fileDependencyGraph: Graph + def __init__( self, name: str, @@ -1034,6 +1122,9 @@ def __init__( self._svVersion = svVersion self._externalVHDLLibraries = [] + self._vhdlLibraryDependencyGraph = Graph() + self._fileDependencyGraph = Graph() + @property def Name(self) -> str: """Property setting or returning the design's name.""" @@ -1096,12 +1187,12 @@ def DefaultFileSet(self) -> FileSet: def DefaultFileSet(self, value: Union[str, FileSet]) -> None: if isinstance(value, str): if (value not in self._fileSets.keys()): - raise Exception("Fileset '{0}' is not in this design.".format(value)) + raise Exception(f"Fileset '{value}' is not in this design.") self._defaultFileSet = self._fileSets[value] elif isinstance(value, FileSet): if (value not in self.FileSets): - raise Exception("Fileset '{0}' is not associated to this design.".format(value)) + raise Exception(f"Fileset '{value}' is not associated to this design.") self._defaultFileSet = value else: @@ -1129,7 +1220,7 @@ def Files(self, fileType: FileType = FileTypes.Any, fileSet: Union[str, FileSet] try: fileSet = self._fileSets[fileSet] except KeyError as ex: - raise Exception("Fileset {name} not bound to design {design}.".format(name=fileSet.Name, design=self.Name)) from ex + raise Exception(f"Fileset {fileSet.Name} not bound to design {self.Name}.") from ex elif not isinstance(fileSet, FileSet): raise TypeError("Parameter 'fileSet' is not of type 'str' or 'FileSet' nor value 'None'.") @@ -1181,8 +1272,8 @@ def __setitem__(self, key: Type[Attribute], value: typing_Any): self._attributes[key] = value @property - def VHDLLibraries(self) -> List[VHDLLibrary]: - return self._vhdlLibraries.values() + def VHDLLibraries(self) -> Dict[str, VHDLLibrary]: + return self._vhdlLibraries @property def VHDLVersion(self) -> VHDLVersion: @@ -1233,7 +1324,7 @@ def AddFileSet(self, fileSet: FileSet) -> None: elif (fileSet in self.FileSets): raise Exception("Design already contains this fileSet.") elif (fileSet.Name in self._fileSets.keys()): - raise Exception("Design already contains a fileset named '{0}'.".format(fileSet.Name)) + raise Exception(f"Design already contains a fileset named '{fileSet.Name}'.") fileSet.Design = self self._fileSets[fileSet.Name] = fileSet @@ -1246,7 +1337,7 @@ def AddFile(self, file: File) -> None: if file.FileSet is None: self._defaultFileSet.AddFile(file) else: - raise ValueError("File '{file.Path!s}' is already part of fileset '{file.FileSet.Name}' and can't be assigned via Design to a default fileset.".format(file=file)) + 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.") def AddFiles(self, files: Iterable[File]) -> None: for file in files: @@ -1264,7 +1355,7 @@ def __str__(self): @export -class Project: +class Project(metaclass=ExtendedType, slots=True): """ A :term:`Project` represents a group of designs and the source files therein. diff --git a/pyproject.toml b/pyproject.toml index 4fbec4ea..9d49cf9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [build-system] requires = [ - "pyTooling >= 1.9.2", - "setuptools >= 35.0.2", - "wheel >= 0.29.0" + "pyTooling >= 5.0.0", + "setuptools >= 60.9.3", + "wheel >= 0.38.1" ] build-backend = "setuptools.build_meta" diff --git a/requirements.txt b/requirements.txt index c63ef1d5..07a160c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,3 @@ -pyTooling>=1.9.2 -pyVHDLModel>=0.14.0 -pySVModel>=0.3.1 +pyTooling >= 5.0.0 +pyVHDLModel >= 0.27.1 +pySVModel>=0.3.5 diff --git a/setup.py b/setup.py index 3429ec0d..e01e5fed 100644 --- a/setup.py +++ b/setup.py @@ -44,5 +44,7 @@ gitHubNamespace=gitHubNamespace, sourceFileWithVersion=packageInformationFile, developmentStatus="beta", - classifiers=list(DEFAULT_CLASSIFIERS) + ["Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)"] + classifiers=list(DEFAULT_CLASSIFIERS) + [ + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)" + ] ) diff --git a/tests/VivadoProject/StopWatch/src/Debouncer.vhdl b/tests/VivadoProject/StopWatch/src/Debouncer.vhdl index 23e987f1..efc1ce17 100644 --- a/tests/VivadoProject/StopWatch/src/Debouncer.vhdl +++ b/tests/VivadoProject/StopWatch/src/Debouncer.vhdl @@ -9,12 +9,12 @@ entity Debouncer is generic ( CLOCK_FREQ : freq := 100 MHz; DEBOUNCE_TIME : time := 3 ms; - + BITS : positive ); port ( Clock : in std_logic; - + Input : in std_logic_vector(BITS - 1 downto 0); Output : out std_logic_vector(BITS - 1 downto 0) := (others => '0') ); @@ -23,7 +23,7 @@ end entity; architecture rtl of Debouncer is constant DEBOUNCE_COUNTER_MAX : positive := TimingToCycles(ite(IS_SIMULATION, 1 us, DEBOUNCE_TIME), CLOCK_FREQ); constant DEBOUNCE_COUNTER_BITS : positive := log2(DEBOUNCE_COUNTER_MAX); - + begin assert false report "CLOCK_FREQ: " & freq'image(CLOCK_FREQ); assert false report "DEBOUNCE_TIME: " & time'image(DEBOUNCE_TIME); @@ -42,7 +42,7 @@ begin else DebounceCounter <= to_signed(DEBOUNCE_COUNTER_MAX - 3, DebounceCounter'length); end if; - + -- latch input bit, if input was stable for DEBOUNCE_TIME_MS if (DebounceCounter(DebounceCounter'high) = '1') then Output(i) <= Input(i); diff --git a/tests/VivadoProject/StopWatch/src/toplevel.StopWatch.vhdl b/tests/VivadoProject/StopWatch/src/toplevel.StopWatch.vhdl index 2ec9806c..069b8662 100644 --- a/tests/VivadoProject/StopWatch/src/toplevel.StopWatch.vhdl +++ b/tests/VivadoProject/StopWatch/src/toplevel.StopWatch.vhdl @@ -13,7 +13,7 @@ entity toplevel is port ( NexysA7_SystemClock : in std_logic; NexysA7_GPIO_Button_Reset_n : in std_logic; - + NexysA7_GPIO_Button : in std_logic_vector(0 downto 0); NexysA7_GPIO_Seg7_Cathode_n : out std_logic_vector(7 downto 0); NexysA7_GPIO_Seg7_Anode_n : out std_logic_vector(7 downto 0) @@ -30,20 +30,20 @@ architecture rtl of toplevel is 4 => (Modulo => 10, Dot => '0'), 5 => (Modulo => 6, Dot => '0') ); - + signal Board_Reset : std_logic; - + signal Deb_Reset : std_logic; signal Deb_Start : std_logic; signal Deb_Start_d : std_logic := '0'; signal Deb_Start_re : std_logic; - + signal Reset : std_logic; signal Start : std_logic; - - + + signal Digits : T_BCD_Vector(STOPWATCH_CONFIGURATION'length - 1 downto 0); - + signal Cathode : std_logic_vector(7 downto 0); signal Anode : std_logic_vector(Digits'range); @@ -59,7 +59,7 @@ begin ) port map ( Clock => NexysA7_SystemClock, - + Input(0) => Board_Reset, Input(1) => NexysA7_GPIO_Button(0), Output(0) => Deb_Reset, @@ -67,19 +67,19 @@ begin ); Reset <= Deb_Reset; - + -- Rising edge detection Deb_Start_d <= Deb_Start when rising_edge(NexysA7_SystemClock); Deb_Start_re <= not Deb_Start_d and Deb_Start; - + -- renaming Start <= Deb_Start_re; - + -- Stopwatch sw: entity work.Stopwatch generic map ( CLOCK_FREQ => CLOCK_FREQ, - + TIMEBASE => 10 ms, CONFIG => STOPWATCH_CONFIGURATION ) @@ -91,7 +91,7 @@ begin Digits => Digits ); - + -- 7-segment display display: entity work.seg7_Display generic map ( @@ -102,7 +102,7 @@ begin Clock => NexysA7_SystemClock, DigitValues => Digits, - + Seg7_Segments => Cathode, Seg7_Selects => Anode ); diff --git a/tests/project/designA/file_A1.vhdl b/tests/project/designA/file_A1.vhdl index e69de29b..0d7a991d 100644 --- a/tests/project/designA/file_A1.vhdl +++ b/tests/project/designA/file_A1.vhdl @@ -0,0 +1,17 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +library libCommon; +use libCommon.P1.all; + +entity A1 is + port ( + signal Clock : in std_logic + ); +end entity; + +architecture rtl of A1 is + +begin + +end architecture; diff --git a/tests/project/designA/file_A2.vhdl b/tests/project/designA/file_A2.vhdl index e69de29b..e48cb1c0 100644 --- a/tests/project/designA/file_A2.vhdl +++ b/tests/project/designA/file_A2.vhdl @@ -0,0 +1,20 @@ +library IEEE; +use IEEE.std_logic_1164.all; + +library libCommon; +use libCommon.P2.all; + +entity A2 is + port ( + signal Clock : in std_logic + ); +end entity; + +architecture rtl of A2 is + +begin + a : entity work.A1 + port map ( + Clock => Clock + ); +end architecture; diff --git a/tests/project/designB/file_B1.vhdl b/tests/project/designB/file_B1.vhdl index e69de29b..31ae73f7 100644 --- a/tests/project/designB/file_B1.vhdl +++ b/tests/project/designB/file_B1.vhdl @@ -0,0 +1,14 @@ + +library libraryCommon; +use libraryCommon.P2.all; + +entity B1 is + +end entity; + +architecture rtl of B1 is + +begin + +end architecture; + diff --git a/tests/project/lib/file_P1.vhdl b/tests/project/lib/file_P1.vhdl new file mode 100644 index 00000000..27a2d8d5 --- /dev/null +++ b/tests/project/lib/file_P1.vhdl @@ -0,0 +1,8 @@ + +package P1 is + +end package; + +package body P1 is + +end package body; diff --git a/tests/project/lib/file_P2.vhdl b/tests/project/lib/file_P2.vhdl new file mode 100644 index 00000000..b9a22059 --- /dev/null +++ b/tests/project/lib/file_P2.vhdl @@ -0,0 +1,10 @@ + +use work.P1.all; + +package P2 is + +end package; + +package body P2 is + +end package body; diff --git a/tests/requirements.txt b/tests/requirements.txt index e71c276a..7719481f 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,12 +1,12 @@ -r ../requirements.txt # Coverage collection -Coverage>=6.2 +Coverage>=7.0 # Test Runner -pytest>=6.2.5 -pytest-cov>=3.0.0 +pytest>=7.2.0 +pytest-cov>=4.0.0 # Static Type Checking -mypy>=0.931 -lxml>=4.6 +mypy>=0.990 +lxml>=4.9 diff --git a/tests/unit/DependencyScan.py b/tests/unit/DependencyScan.py new file mode 100644 index 00000000..d06dc9c7 --- /dev/null +++ b/tests/unit/DependencyScan.py @@ -0,0 +1,104 @@ +# ==================================================================================================================== # +# _____ ____ _ _ ____ _ _ __ __ _ _ # +# _ __ _ _| ____| _ \ / \ / \ | _ \ _ __ ___ (_) ___ ___| |_| \/ | ___ __| | ___| | # +# | '_ \| | | | _| | | | |/ _ \ / _ \ | |_) | '__/ _ \| |/ _ \/ __| __| |\/| |/ _ \ / _` |/ _ \ | # +# | |_) | |_| | |___| |_| / ___ \ / ___ \ _| __/| | | (_) | | __/ (__| |_| | | | (_) | (_| | __/ | # +# | .__/ \__, |_____|____/_/ \_\/_/ \_(_)_| |_| \___// |\___|\___|\__|_| |_|\___/ \__,_|\___|_| # +# |_| |___/ |__/ # +# ==================================================================================================================== # +# Authors: # +# Patrick Lehmann # +# # +# License: # +# ==================================================================================================================== # +# Copyright 2017-2023 Patrick Lehmann - Boetzingen, Germany # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +# SPDX-License-Identifier: Apache-2.0 # +# ==================================================================================================================== # +# +"""Instantiation tests for the project model.""" +from pathlib import Path +from unittest import TestCase + +from pytest import mark +from pyVHDLModel import VHDLVersion + +from pyEDAA.ProjectModel import Design, VHDLLibrary, Project, VHDLSourceFile, VerilogSourceFile, FileSet + +try: + from pyGHDL.libghdl import LibGHDLException + from pyGHDL.dom import DOMException + from pyGHDL.dom.NonStandard import Design as DOMDesign, Document + + withGHDL = True +except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover + withGHDL = False + + +if __name__ == "__main__": # pragma: no cover + print("ERROR: you called a testcase declaration file as an executable module.") + print("Use: 'python -m unitest '") + exit(1) + + +class VHDL(TestCase): + @mark.skipif(withGHDL is False, reason="No 'pyGHDL' package found.") + def test_VHDLLibrary(self): + project = Project("project", rootDirectory=Path("project"), vhdlVersion=VHDLVersion.VHDL2019) + designA = Design("designA", directory=Path("designA"), project=project) + fileSetA = FileSet("fileSetA", directory=Path("."), design=designA) + fileSetC = FileSet("fileSetC", directory=Path("../lib"), design=designA) + libraryA = VHDLLibrary("libA", design=designA) + libraryC = VHDLLibrary("libCommon", design=designA) + + fileA1 = VHDLSourceFile(Path("file_A1.vhdl"), fileSet=fileSetA, vhdlLibrary=libraryA) + fileA2 = VHDLSourceFile(Path("file_A2.vhdl"), fileSet=fileSetA, vhdlLibrary=libraryA) + fileA3 = VerilogSourceFile(Path("file_A3.v"), fileSet=fileSetA) + + fileP1 = VHDLSourceFile(Path("file_P1.vhdl"), fileSet=fileSetC, vhdlLibrary=libraryC) + fileP2 = VHDLSourceFile(Path("file_P2.vhdl"), fileSet=fileSetC, vhdlLibrary=libraryC) + + print() + print(f"Loading design '{designA.Name}':") + design = DOMDesign(name=designA.Name) + + print(f" Loading default libraries (Std, Ieee, ...)") + design.LoadDefaultLibraries() + + for libraryName, vhdlLibrary in designA.VHDLLibraries.items(): + print(f" Loading library '{libraryName}' ...") + lib = design.GetLibrary(libraryName) + + for file in vhdlLibrary.Files: + print(f" Parsing '{file.ResolvedPath}' ...") + try: + vhdlDocument = Document(file.ResolvedPath) + except DOMException as ex: + if isinstance(ex.__cause__, LibGHDLException): + print(ex.__cause__) + for message in ex.__cause__.InternalErrors: + print(f" {message}") + else: + print(ex) + + design.AddDocument(vhdlDocument, lib) + + print(f" Analyzing design ...") + design.Analyze() + + print() + print(f"Toplevel: {design.TopLevel}") + hierarchy = design.TopLevel.HierarchyVertex.ConvertToTree() + print(hierarchy.Render()) diff --git a/tests/unit/Design.py b/tests/unit/Design.py index 47113898..bc80e6cf 100644 --- a/tests/unit/Design.py +++ b/tests/unit/Design.py @@ -141,7 +141,7 @@ def test_Files(self): class Validate(TestCase): def test_Design(self): - project = Project("project", rootDirectory=Path("tests/project")) + project = Project("project", rootDirectory=Path("project")) design = Design("design", directory=Path("designA"), project=project) design.Validate() diff --git a/tests/unit/File.py b/tests/unit/File.py index 0b45210b..84897f92 100644 --- a/tests/unit/File.py +++ b/tests/unit/File.py @@ -147,7 +147,7 @@ def test_ResolveDirectory(self): class Validate(TestCase): def test_File(self): - project = Project("project", rootDirectory=Path("tests/project")) + project = Project("project", rootDirectory=Path("project")) design = Design("design", directory=Path("designA"), project=project) fileSet = FileSet("fileset", design=design) file = File(Path("file_A1.vhdl"), fileSet=fileSet) @@ -157,7 +157,7 @@ def test_File(self): class Attributes(TestCase): def test_AttachedToFile(self): - project = Project("project", rootDirectory=Path("tests/project")) + project = Project("project", rootDirectory=Path("project")) design = Design("design", directory=Path("designA"), project=project) fileSet = FileSet("fileset", design=design) file = File(Path("file_A1.vhdl"), fileSet=fileSet) @@ -171,7 +171,7 @@ def test_AttachedToFile(self): self.assertEqual("5", file[KeyValueAttribute]["id1"]) def test_AttachedToFileSet(self): - project = Project("project", rootDirectory=Path("tests/project")) + project = Project("project", rootDirectory=Path("project")) design = Design("design", directory=Path("designA"), project=project) fileSet = FileSet("fileset", design=design) file = File(Path("file_A1.vhdl"), fileSet=fileSet) diff --git a/tests/unit/FileSet.py b/tests/unit/FileSet.py index 837e3598..874c94e6 100644 --- a/tests/unit/FileSet.py +++ b/tests/unit/FileSet.py @@ -233,7 +233,7 @@ def test_SourceFile(self): class Validate(TestCase): def test_FileSet(self): - project = Project("project", rootDirectory=Path("tests/project")) + project = Project("project", rootDirectory=Path("project")) design = Design("design", directory=Path("designA"), project=project) fileSet = FileSet("fileset", design=design) diff --git a/tests/unit/Files.py b/tests/unit/Files.py index 76cd04c7..f5ae3a0d 100644 --- a/tests/unit/Files.py +++ b/tests/unit/Files.py @@ -97,7 +97,7 @@ def test_GetVersionFromFileSet(self): self.assertEqual(vhdlVersion, file.VHDLVersion) def test_Validate(self): - project = Project("project", rootDirectory=Path("tests/project"), vhdlVersion=VHDLVersion.VHDL2019) + project = Project("project", rootDirectory=Path("project"), vhdlVersion=VHDLVersion.VHDL2019) design = Design("design", directory=Path("designA"), project=project) vhdlLibrary = VHDLLibrary("library", design=design) fileSet = FileSet("fileset", vhdlLibrary=vhdlLibrary, design=design) diff --git a/tests/unit/Project.py b/tests/unit/Project.py index a6caa958..a94d3e45 100644 --- a/tests/unit/Project.py +++ b/tests/unit/Project.py @@ -110,6 +110,6 @@ def test_ResolveDirectory(self): class Validate(TestCase): def test_Project(self): - project = Project("project", rootDirectory=Path("tests/project")) + project = Project("project", rootDirectory=Path("project")) project.Validate() diff --git a/tests/unit/VHDLLibrary.py b/tests/unit/VHDLLibrary.py index be100f2d..0ad1f3a1 100644 --- a/tests/unit/VHDLLibrary.py +++ b/tests/unit/VHDLLibrary.py @@ -64,8 +64,7 @@ def test_VHDLLibraryFromProject(self): library = VHDLLibrary("library", project=project) self.assertIs(project, library.Project) - self.assertIsNone(library.Design) - + self.assertIs(project.DefaultDesign, library.Design) def test_VHDLLibraryFromProjectAndDesign(self): project = Project("project") diff --git a/tests/unit/VivadoProject.py b/tests/unit/VivadoProject.py index 55066e18..221d93df 100644 --- a/tests/unit/VivadoProject.py +++ b/tests/unit/VivadoProject.py @@ -42,7 +42,7 @@ class FileSets(TestCase): def test_Parsing(self): - xprPath = Path.cwd() / "tests/VivadoProject/StopWatch/project/StopWatch.xpr" + xprPath = Path.cwd() / "VivadoProject/StopWatch/project/StopWatch.xpr" # print() # print(f"{xprPath}") xprFile = VivadoProjectFile(xprPath)