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)