diff --git a/.btd.yml b/.btd.yml deleted file mode 100644 index e3509f78..00000000 --- a/.btd.yml +++ /dev/null @@ -1,8 +0,0 @@ -input: doc -output: _build -requirements: requirements.txt -target: gh-pages -formats: [ html ] -images: - base: btdi/sphinx:pytooling -theme: https://codeload.GitHub.com/buildthedocs/sphinx.theme/tar.gz/v1 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..6d63ee37 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +* @Paebbels + +/.github/ @Paebbels diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0e75a2d3..d982b019 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,9 +8,6 @@ updates: prefix: "[Dependabot]" labels: - Dependencies - reviewers: - - Paebbels - - Umarcor schedule: interval: "daily" # Checks on Monday trough Friday. @@ -22,8 +19,5 @@ updates: 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 ac698f89..4ed2d900 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,16 +1,30 @@ # New Features - + +* tbd * tbd # Changes +* tbd * tbd # Bug Fixes +* tbd +* tbd + +# Documentation + +* tbd +* tbd + +# Unit Tests + +* tbd * tbd ---------- -# Related PRs: +# Related Issues and Pull-Requests * tbd +* tbd diff --git a/.github/workflows/Pipeline.yml b/.github/workflows/Pipeline.yml index d7d08455..9f12dfd8 100644 --- a/.github/workflows/Pipeline.yml +++ b/.github/workflows/Pipeline.yml @@ -4,136 +4,28 @@ on: push: workflow_dispatch: schedule: - - cron: '0 0 * * 5' +# Every Friday at 22:00 - rerun pipeline to check for dependency-based issues + - cron: '0 22 * * 5' -jobs: - - Params: - uses: pyTooling/Actions/.github/workflows/Parameters.yml@r0 - with: - name: pyEDAA.UCIS - - UnitTesting: - uses: pyTooling/Actions/.github/workflows/UnitTesting.yml@r0 - needs: - - Params - with: - jobs: ${{ needs.Params.outputs.python_jobs }} - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.unittesting }} - - Coverage: - uses: pyTooling/Actions/.github/workflows/CoverageCollection.yml@r0 - needs: - - Params - with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.coverage }} - secrets: - codacy_token: ${{ secrets.CODACY_PROJECT_TOKEN }} - - StaticTypeCheck: - uses: pyTooling/Actions/.github/workflows/StaticTypeCheck.yml@r0 - needs: - - Params - with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - requirements: '-r tests/requirements.txt' - commands: | - cd pyEDAA - mypy --html-report ../htmlmypy -p UCIS - report: 'htmlmypy' - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.typing }} - - PublishTestResults: - uses: pyTooling/Actions/.github/workflows/PublishTestResults.yml@r0 - needs: - - UnitTesting +permissions: + actions: write + contents: write + pages: write + id-token: write - Package: - uses: pyTooling/Actions/.github/workflows/Package.yml@r0 - needs: - - Params - - Coverage - with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} - - Release: - uses: pyTooling/Actions/.github/workflows/Release.yml@r0 - if: startsWith(github.ref, 'refs/tags') - needs: - - UnitTesting - - Coverage - - StaticTypeCheck - - Package - - PublishOnPyPI: - uses: pyTooling/Actions/.github/workflows/PublishOnPyPI.yml@r0 - if: startsWith(github.ref, 'refs/tags') - needs: - - Params - - Release - - Package - with: - python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - requirements: -r dist/requirements.txt - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} +jobs: + Pipeline: + uses: pyTooling/Actions/.github/workflows/CompletePipeline.yml@r7 + with: + package_namespace: 'pyEDAA' + package_name: 'UCIS' + unittest_python_version_list: '3.11 3.12 3.13 3.14 pypy-3.11' + bandit: 'true' + pylint: 'true' + codecov: 'true' + codacy: 'true' + dorny: 'true' secrets: - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - -# VerifyDocs: -# uses: pyTooling/Actions/.github/workflows/VerifyDocs.yml@r0 -# needs: -# - Params -# with: -# python_version: ${{ fromJson(needs.Params.outputs.params).python_version }} - - BuildTheDocs: - uses: pyTooling/Actions/.github/workflows/BuildTheDocs.yml@r0 - needs: - - Params -# - VerifyDocs - with: - artifact: ${{ fromJson(needs.Params.outputs.params).artifacts.doc }} - - PublishToGitHubPages: - uses: pyTooling/Actions/.github/workflows/PublishToGitHubPages.yml@r0 - 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 }} - - ArtifactCleanUp: - uses: pyTooling/Actions/.github/workflows/ArtifactCleanUp.yml@r0 - needs: - - Params - - UnitTesting - - Coverage - - StaticTypeCheck - - BuildTheDocs - - PublishToGitHubPages - - PublishTestResults - with: - package: ${{ fromJson(needs.Params.outputs.params).artifacts.package }} - 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 }} + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + CODACY_TOKEN: ${{ secrets.CODACY_TOKEN }} diff --git a/.gitignore b/.gitignore index 0e00965a..e0f9e7c8 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,13 @@ __pycache__/ .coverage .cov coverage.xml +/report/coverage + +# mypy +/report/typing + +# pytest +/report/unit # setuptools /build/**/*.* @@ -21,5 +28,8 @@ coverage.xml /doc/pyEDAA.UCIS/**/*.* !/doc/pyEDAA.UCIS/index.rst -# PyCharm project +# PyCharm project files /.idea/workspace.xml + +# Git files +!.git* diff --git a/.idea/pyEDAA.UCIS.iml b/.idea/pyEDAA.UCIS.iml index aad2a96d..398dee45 100644 --- a/.idea/pyEDAA.UCIS.iml +++ b/.idea/pyEDAA.UCIS.iml @@ -2,13 +2,16 @@ + - + + + - + \ No newline at end of file diff --git a/README.md b/README.md index d32b1048..b6752104 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![PyPI](https://img.shields.io/pypi/v/pyEDAA.UCIS?longCache=true&style=flat-square&logo=PyPI&logoColor=FBE072)](https://pypi.org/project/pyEDAA.UCIS/) ![PyPI - Status](https://img.shields.io/pypi/status/pyEDAA.UCIS?longCache=true&style=flat-square&logo=PyPI&logoColor=FBE072) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyEDAA.UCIS?longCache=true&style=flat-square&logo=PyPI&logoColor=FBE072) -[![GitHub Workflow - Build and Test Status](https://img.shields.io/github/workflow/status/edaa-org/pyEDAA.UCIS/Pipeline/main?longCache=true&style=flat-square&label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF)](https://GitHub.com/edaa-org/pyEDAA.UCIS/actions/workflows/Pipeline.yml) +[![GitHub Workflow - Build and Test Status](https://img.shields.io/github/actions/workflow/status/edaa-org/pyEDAA.UCIS/Pipeline.yml?longCache=true&style=flat-square&label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF)](https://GitHub.com/edaa-org/pyEDAA.UCIS/actions/workflows/Pipeline.yml) [![Libraries.io status for latest release](https://img.shields.io/librariesio/release/pypi/pyEDAA.UCIS?longCache=true&style=flat-square&logo=Libraries.io&logoColor=fff)](https://libraries.io/github/edaa-org/pyEDAA.UCIS) [![Codacy - Quality](https://img.shields.io/codacy/grade/63bd2bd65585447a9f6d7ad4e7d82a35?longCache=true&style=flat-square&logo=Codacy)](https://app.codacy.com/gh/edaa-org/pyEDAA.UCIS) [![Codacy - Coverage](https://img.shields.io/codacy/coverage/63bd2bd65585447a9f6d7ad4e7d82a35?longCache=true&style=flat-square&logo=Codacy)](https://app.codacy.com/gh/edaa-org/pyEDAA.UCIS) @@ -27,7 +27,7 @@ Unified Coverage Interoperability Standard (UCIS) ## Main Goals -* Parse UCDB files and provide a UCDB data model. +* Convert UCDB files and provide a UCDB data model. * Export and convert data from UCDB to Cobertura format. * Also support flavors not following the Unified Coverage Interoperability Standard (UCIS). diff --git a/dist/requirements.txt b/dist/requirements.txt index 6c4932c1..e87980bd 100644 --- a/dist/requirements.txt +++ b/dist/requirements.txt @@ -1,2 +1,2 @@ -wheel -twine +wheel ~= 0.45.0 +twine ~= 6.2 diff --git a/doc/CodeCoverage.rst b/doc/CodeCoverage.rst index afceecca..217fd1e4 100644 --- a/doc/CodeCoverage.rst +++ b/doc/CodeCoverage.rst @@ -1,18 +1,25 @@ -Code Coverage -############# +.. _CODECOV: -Statement Coverage -****************** +Code Coverage Report +#################### -.. code-block:: Bash +.. grid:: 2 - # Convert ACDB file into UCDB file (XML format) - acdb2xml -i aggregate.acdb -o ucdb.xml + .. grid-item:: + :columns: 8 - # Convert UCDB file into Cobertura format - pyedaa-ucis export --ucdb ucdb.xml --cobertura cobertura.xml + .. report:code-coverage:: + :reportid: src -Branch Coverage -*************** + .. grid-item:: + :columns: 4 -.. note:: Branch coverage isn't supported yet. + .. report:code-coverage-legend:: + :reportid: src + :style: vertical-table + +---------- + +Code coverage report generated with `pytest `__, +`Coverage.py `__ and visualized by +`sphinx-reports `__. diff --git a/doc/CommandLineInterface.rst b/doc/CommandLineInterface.rst index e60a0532..41f416dd 100644 --- a/doc/CommandLineInterface.rst +++ b/doc/CommandLineInterface.rst @@ -1,7 +1,7 @@ Command Line Interfaces ####################### -.. _References:cli: +.. _References/cli: .. autoprogram:: pyEDAA.UCIS.CLI:Program().MainParser :prog: pyedaa-ucis diff --git a/doc/Dependency.rst b/doc/Dependency.rst index d758c4e8..6a2930f6 100644 --- a/doc/Dependency.rst +++ b/doc/Dependency.rst @@ -27,12 +27,15 @@ pyEDAA.UCIS Package +---------------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Package** | **Version** | **License** | **Dependencies** | +===============================================================+=============+===========================================================================================+========================================================================================================================================================+ -| `pyTooling `__ | ≥1.9.5 | `Apache License, 2.0 `__ | *None* | -+---------------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `pyAttributes `__ | ≥2.5.1 | `Apache License, 2.0 `__ | * `pyTooling `__ - `Apache License, 2.0 `__ | -| | | | * `argcomplete `__ - `Apache License, 2.0 `__ | +| `pyTooling `__ | ≥8.0 | `Apache License, 2.0 `__ | *None* | +---------------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ +.. todo:: investigate dependencies and licenses of pyTooling with pyAttributes/argcomplete. + +.. | `pyAttributes `__ | ≥2.5.1 | `Apache License, 2.0 `__ | * `pyTooling `__ - `Apache License, 2.0 `__ | + | | | | * `argcomplete `__ - `Apache License, 2.0 `__ | + +---------------------------------------------------------------+-------------+-------------------------------------------------------------------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------+ + .. _dependency-testing: @@ -55,19 +58,21 @@ the mandatory dependencies too. .. rubric:: Dependency List -+-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| **Package** | **Version** | **License** | **Dependencies** | -+===========================================================+=============+========================================================================================+======================+ -| `pytest `__ | ≥7.0.1 | `MIT `__ | *Not yet evaluated.* | -+-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `pytest-cov `__ | ≥3.0.0 | `MIT `__ | *Not yet evaluated.* | -+-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `Coverage `__ | ≥6.3 | `Apache License, 2.0 `__ | *Not yet evaluated.* | -+-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `mypy `__ | ≥0.931 | `MIT `__ | *Not yet evaluated.* | -+-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ -| `lxml `__ | ≥4.8 | `BSD 3-Clause `__ | *Not yet evaluated.* | -+-----------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ +| **Package** | **Version** | **License** | **Dependencies** | ++=====================================================================+=============+========================================================================================+======================+ +| `pytest `__ | ≥8.3 | `MIT `__ | *Not yet evaluated.* | ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ +| `pytest-cov `__ | ≥6.1 | `MIT `__ | *Not yet evaluated.* | ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ +| `Coverage `__ | ≥7.8 | `Apache License, 2.0 `__ | *Not yet evaluated.* | ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ +| `mypy `__ | ≥1.15 | `MIT `__ | *Not yet evaluated.* | ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ +| `typing-extensions `__ | ≥4.13 | `PSF-2.0 `__ | *Not yet evaluated.* | ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ +| `lxml `__ | ≥5.4 | `BSD 3-Clause `__ | *Not yet evaluated.* | ++---------------------------------------------------------------------+-------------+----------------------------------------------------------------------------------------+----------------------+ .. _dependency-documentation: @@ -94,15 +99,21 @@ the mandatory dependencies too. +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Package** | **Version** | **License** | **Dependencies** | +=================================================================================================+==============+==========================================================================================================+======================================================================================================================================================+ -| `pyTooling `__ | ≥1.9.5 | `Apache License, 2.0 `__ | *None* | +| `pyTooling `__ | ≥8.4 | `Apache License, 2.0 `__ | *None* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `Sphinx `__ | ≥4.4.0 | `BSD 3-Clause `__ | *Not yet evaluated.* | +| `Sphinx `__ | ≥8.2 | `BSD 3-Clause `__ | *Not yet evaluated.* | ++-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ +| `sphinxcontrib-mermaid `__ | ≥1.0 | `BSD `__ | *Not yet evaluated.* | ++-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ +| `autoapi `__ | ≥2.0.1 | `Apache License, 2.0 `__ | *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_design `__ | ≥0.6 | `MIT `__ | *Not yet evaluated.* | ++-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ +| `sphinx-copybutton `__ | ≥0.5.2 | `MIT `__ | *Not yet evaluated.* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `sphinx_autodoc_typehints `__ | ≥1.17.0 | `MIT `__ | *Not yet evaluated.* | +| `sphinx_autodoc_typehints `__ | ≥3.2 | `MIT `__ | *Not yet evaluated.* | +-------------------------------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -130,9 +141,9 @@ install the mandatory dependencies too. +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ | **Package** | **Version** | **License** | **Dependencies** | +============================================================================+==============+==========================================================================================================+======================================================================================================================================================+ -| `pyTooling `__ | ≥1.9.5 | `Apache License, 2.0 `__ | *None* | +| `pyTooling `__ | ≥8.4 | `Apache License, 2.0 `__ | *None* | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ -| `wheel `__ | any | `MIT `__ | *Not yet evaluated.* | +| `wheel `__ | ≥0.45 | `MIT `__ | *Not yet evaluated.* | +----------------------------------------------------------------------------+--------------+----------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -161,7 +172,7 @@ install the mandatory dependencies too. +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ | **Package** | **Version** | **License** | **Dependencies** | +==========================================================+==============+===========================================================================================+======================+ -| `wheel `__ | any | `MIT `__ | *Not yet evaluated.* | +| `wheel `__ | ≥0.45 | `MIT `__ | *Not yet evaluated.* | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ -| `Twine `__ | any | `Apache License, 2.0 `__ | *Not yet evaluated.* | +| `Twine `__ | ≥6.1 | `Apache License, 2.0 `__ | *Not yet evaluated.* | +----------------------------------------------------------+--------------+-------------------------------------------------------------------------------------------+----------------------+ diff --git a/doc/DocCoverage.rst b/doc/DocCoverage.rst new file mode 100644 index 00000000..d2ee3882 --- /dev/null +++ b/doc/DocCoverage.rst @@ -0,0 +1,24 @@ +.. _DOCCOV: + +Documentation Coverage Report +############################# + +.. grid:: 2 + + .. grid-item:: + :columns: 5 + + .. report:doc-coverage:: + :reportid: src + + .. grid-item:: + :columns: 7 + + .. report:doc-coverage-legend:: + :reportid: src + :style: vertical-table + +---------- + +Documentation coverage generated with `"""docstr-coverage""" `__ and +visualized by `sphinx-reports `__. diff --git a/doc/TODO.rst b/doc/TODO.rst new file mode 100644 index 00000000..3144da04 --- /dev/null +++ b/doc/TODO.rst @@ -0,0 +1,4 @@ +TODOs +##### + +.. todolist:: diff --git a/doc/_static/css/override.css b/doc/_static/css/override.css new file mode 100644 index 00000000..4dd6beb5 --- /dev/null +++ b/doc/_static/css/override.css @@ -0,0 +1,115 @@ +/* theme overrides */ +.rst-content h1, +.rst-content h2 { + margin-top: 24px; + margin-bottom: 6px; + text-decoration: underline; +} + +.rst-content h3, +.rst-content h4, +.rst-content h5, +.rst-content h6 { + margin-top: 12px; + margin-bottom: 6px; +} + +.rst-content p { + margin-bottom: 6px +} + +.rst-content .topic-title { + font-size: larger; + font-weight: 700; + margin-top: 18px; + margin-bottom: 6px; +} + +.rst-content p.rubric { + text-decoration: underline; + font-weight: 700; + margin-top: 18px; + margin-bottom: 16px; +} + +/* general overrides */ +html { + font-size: 15px; +} + +footer { + font-size: 95%; + text-align: center +} + +footer p { + margin-bottom: 0px /* 12px */; + font-size: 95% +} + +section > p, +.section p, +.simple li { + text-align: justify +} + +/* wyrm overrides */ +.wy-menu-vertical header, +.wy-menu-vertical p.caption { + color: #9b9b9b /* #55a5d9 */; + padding: 0 0.809em /* 0 1.618em */; + margin: 6px 0 0 0 /* 12px 0 0 */; + border-top: 1px solid #9b9b9b; +} + +.wy-side-nav-search { + margin-bottom: 0 /* .809em */; + background-color: #333333 /* #2980b9 */; + /* BTD: */ + /*color: #fcfcfc*/ +} + +.wy-side-nav-search input[type=text] { + border-radius: 0px /* 50px */; +} + +.wy-side-nav-search .wy-dropdown > a, .wy-side-nav-search > a { + /* BTD: */ + /*color: #fcfcfc;*/ + margin-bottom: 0.404em /* .809em */; +} + +.wy-side-nav-search > div.version { + margin: 0 0 6px 0; + /* BTD: */ + /*margin-top: -.4045em;*/ +} + +.wy-nav .wy-menu-vertical a:hover { + background-color: #333333 /* #2980b9 */; +} + +.wy-nav-content { + max-width: 1600px /* 800px */ ; +} + +.wy-nav-top { + background: #333333 /* #2980b9 */; +} + +/* Sphinx Design */ +.sd-tab-set { + margin: 0 +} + +.sd-tab-set > label { + padding-top: .5em; + padding-right: 1em; + padding-bottom: .5em; + padding-left: 1em +} + +.sd-container-fluid { + padding-left: 0; + padding-right: 0; +} diff --git a/doc/_templates/autoapi/module.rst b/doc/_templates/autoapi/module.rst index 5f9e361b..4dded81f 100644 --- a/doc/_templates/autoapi/module.rst +++ b/doc/_templates/autoapi/module.rst @@ -1,10 +1,12 @@ -.. # Template modified by Patrick Lehmann +.. # 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 }}== +{{ '=' * node.name|length }} +{{ node.name }} +{{ '=' * node.name|length }} + +.. automodule:: {{ node.name }} {##} {%- block modules -%} @@ -12,8 +14,8 @@ **Submodules** - .. toctree:: + :maxdepth: 1 {% for item in subnodes %} {{ item.name }} {%- endfor %} @@ -23,7 +25,17 @@ {##} .. currentmodule:: {{ node.name }} {##} -{%- block functions -%} + +{%- if node.variables %} + +**Variables** + +{% for item, obj in node.variables.items() -%} +- :py:data:`{{ item }}` + {#{ obj|summary }#} +{% endfor -%} +{%- endif -%} + {%- if node.functions %} **Functions** @@ -33,15 +45,19 @@ {{ obj|summary }} {% endfor -%} +{%- endif -%} -{% for item in node.functions %} -.. autofunction:: {{ item }} -{##} -{%- endfor -%} +{%- if node.exceptions %} + +**Exceptions** + +{% for item, obj in node.exceptions.items() -%} +- :py:exc:`{{ item }}`: + {{ obj|summary }} + +{% endfor -%} {%- endif -%} -{%- endblock -%} -{%- block classes -%} {%- if node.classes %} **Classes** @@ -51,14 +67,40 @@ {{ obj|summary }} {% endfor -%} +{%- endif -%} -{% for item in node.classes %} -.. autoclass:: {{ item }} - :members: +{%- block variables -%} +{%- if node.variables %} - .. rubric:: Inheritance - .. inheritance-diagram:: {{ item }} - :parts: 1 +--------------------- + +**Variables** + +{#% for item, obj in node.variables.items() -%} +- :py:data:`{{ item }}` +{% endfor -%#} + +{% for item, obj in node.variables.items() %} +.. autodata:: {{ item }} + :annotation: + + .. code-block:: text + + {{ obj|pprint|indent(6) }} +{##} +{%- endfor -%} +{%- endif -%} +{%- endblock -%} + +{%- block functions -%} +{%- if node.functions %} + +--------------------- + +**Functions** + +{% for item in node.functions %} +.. autofunction:: {{ item }} {##} {%- endfor -%} {%- endif -%} @@ -67,13 +109,15 @@ {%- block exceptions -%} {%- if node.exceptions %} +--------------------- + **Exceptions** -{% for item, obj in node.exceptions.items() -%} +{#% for item, obj in node.exceptions.items() -%} - :py:exc:`{{ item }}`: {{ obj|summary }} -{% endfor -%} +{% endfor -%#} {% for item in node.exceptions %} .. autoexception:: {{ item }} @@ -86,22 +130,30 @@ {%- endif -%} {%- endblock -%} -{%- block variables -%} -{%- if node.variables %} +{%- block classes -%} +{%- if node.classes %} -**Variables** +--------------------- -{% for item, obj in node.variables.items() -%} -- :py:data:`{{ item }}` -{% endfor -%} +**Classes** -{% for item, obj in node.variables.items() %} -.. autodata:: {{ item }} - :annotation: +{#% for item, obj in node.classes.items() -%} +- :py:class:`{{ item }}`: + {{ obj|summary }} - .. code-block:: text +{% endfor -%#} - {{ obj|pprint|indent(6) }} +{% for item in node.classes %} +.. autoclass:: {{ item }} + :members: + :private-members: + :special-members: + :inherited-members: + :exclude-members: __weakref__ + + .. rubric:: Inheritance + .. inheritance-diagram:: {{ item }} + :parts: 1 {##} {%- endfor -%} {%- endif -%} diff --git a/doc/_templates/autoapi/package.rst b/doc/_templates/autoapi/package.rst new file mode 100644 index 00000000..9cc9fbdc --- /dev/null +++ b/doc/_templates/autoapi/package.rst @@ -0,0 +1,14 @@ +.. # Template created by Patrick Lehmann + +Python Class Reference +###################### + +Reference of all packages and modules: + +.. automodule:: {{ node.name }} + +.. toctree:: + :maxdepth: 1 +{% for item in subnodes %} + {{ item.name }} +{%- endfor %} diff --git a/doc/conf.py b/doc/conf.py index d210b78d..9deee418 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,19 +1,27 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. - from sys import path as sys_path from os.path import abspath from pathlib import Path -from json import loads from pyTooling.Packaging import extractVersionInformation +# ============================================================================== +# Project configuration +# ============================================================================== +githubNamespace = "edaa-org" +project = "pyEDAA.UCIS" +directoryName = project.replace('.', '/') + +# ============================================================================== +# Project paths +# ============================================================================== ROOT = Path(__file__).resolve().parent -sys_path.insert(0, abspath('.')) -sys_path.insert(0, abspath('..')) -sys_path.insert(0, abspath('../pyEDAA/UCIS')) +sys_path.insert(0, abspath(".")) +sys_path.insert(0, abspath("..")) +sys_path.insert(0, abspath(f"../{directoryName}")) # ============================================================================== @@ -22,9 +30,7 @@ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -project = "pyEDAA.UCIS" - -packageInformationFile = Path(f"../{project.replace('.', '/')}/__init__.py") +packageInformationFile = Path(f"../{directoryName}/__init__.py") versionInformation = extractVersionInformation(packageInformationFile) author = versionInformation.Author @@ -32,38 +38,39 @@ version = ".".join(versionInformation.Version.split(".")[:2]) # e.g. 2.3 The short X.Y version. release = versionInformation.Version + # ============================================================================== # Miscellaneous settings # ============================================================================== # The master toctree document. -master_doc = 'index' +master_doc = "index" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [ "_build", - "_themes", + "_theme", "Thumbs.db", ".DS_Store" ] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'stata-dark' +pygments_style = "manni" # ============================================================================== # 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", encoding="utf-8") 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 = "" @@ -71,39 +78,38 @@ # ============================================================================== # Options for HTML output # ============================================================================== - -html_context = {} -ctx = ROOT / 'context.json' -if ctx.is_file(): - html_context.update(loads(ctx.open('r').read())) - -if (ROOT / "_theme").is_dir(): - html_theme_path = ["."] - html_theme = "_theme" - html_theme_options = { - 'logo_only': True, - 'home_breadcrumbs': False, - 'vcs_pageview_mode': 'blob', - } -else: - html_theme = "alabaster" +html_theme = "sphinx_rtd_theme" +html_theme_options = { + "logo_only": True, + "vcs_pageview_mode": 'blob', + "navigation_depth": 5, +} +html_css_files = [ + 'css/override.css', +] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] html_logo = str(Path(html_static_path[0]) / "logo.svg") html_favicon = str(Path(html_static_path[0]) / "favicon.svg") # Output file base name for HTML help builder. -htmlhelp_basename = 'pyEDAAUCISDoc' +htmlhelp_basename = f"{project}Doc" # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. html_last_updated_fmt = "%d.%m.%Y" +# ============================================================================== +# Python settings +# ============================================================================== +modindex_common_prefix = [ + f"{project}." +] # ============================================================================== # Options for LaTeX / PDF output @@ -112,13 +118,13 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - 'papersize': 'a4paper', + "papersize": "a4paper", # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. - 'preamble': dedent(r""" + "preamble": dedent(r""" % ================================================================================ % User defined additional preamble code % ================================================================================ @@ -144,10 +150,10 @@ # author, documentclass [howto, manual, or own class]). latex_documents = [ ( master_doc, - 'pyEDAA.UCIS.tex', - 'The pyEDAA.UCIS Documentation', - 'Patrick Lehmann', - 'manual' + f"{project}.tex", + f"The {project} Documentation", + f"Patrick Lehmann", + f"manual" ), ] @@ -158,21 +164,24 @@ extensions = [ # Standard Sphinx extensions "sphinx.ext.autodoc", - 'sphinx.ext.extlinks', - 'sphinx.ext.intersphinx', - 'sphinx.ext.inheritance_diagram', - 'sphinx.ext.todo', - 'sphinx.ext.graphviz', - 'sphinx.ext.mathjax', - 'sphinx.ext.ifconfig', - 'sphinx.ext.viewcode', + "sphinx.ext.extlinks", + "sphinx.ext.intersphinx", + "sphinx.ext.inheritance_diagram", + "sphinx.ext.todo", + "sphinx.ext.graphviz", + "sphinx.ext.mathjax", + "sphinx.ext.ifconfig", + "sphinx.ext.viewcode", # SphinxContrib extensions - 'sphinxcontrib.autoprogram', - 'sphinxcontrib.mermaid', + "sphinxcontrib.mermaid", + "sphinxcontrib.autoprogram", # Other extensions - 'autoapi.sphinx', - 'sphinx_fontawesome', - 'sphinx_autodoc_typehints', + "sphinx_design", + "sphinx_copybutton", + "sphinx_autodoc_typehints", + "autoapi.sphinx", + "sphinx_reports", +# User defined extensions ] @@ -180,7 +189,8 @@ # Sphinx.Ext.InterSphinx # ============================================================================== intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), + "python": ("https://docs.python.org/3", None), + "pyTool": ("https://pyTooling.github.io/pyTooling/", None), } @@ -188,16 +198,28 @@ # 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" # ============================================================================== # Sphinx.Ext.ExtLinks # ============================================================================== extlinks = { - "ghissue": ('https://GitHub.com/edaa-org/pyEDAA.UCIS/issues/%s', 'issue #'), - "ghpull": ('https://GitHub.com/edaa-org/pyEDAA.UCIS/pull/%s', 'pull request #'), - "ghsrc": ('https://GitHub.com/edaa-org/pyEDAA.UCIS/blob/main/%s?ts=2', None), + "gh": (f"https://GitHub.com/%s", "%s"), + "ghissue": (f"https://GitHub.com/{githubNamespace}/{project}/issues/%s", "issue #%s"), + "ghpull": (f"https://GitHub.com/{githubNamespace}/{project}/pull/%s", "pull request #%s"), + "ghsrc": (f"https://GitHub.com/{githubNamespace}/{project}/blob/main/%s", None), + "pypi": ('https://PyPI.org/project/%s', '%s'), + "wiki": (f"https://en.wikipedia.org/wiki/%s", None), } @@ -207,6 +229,26 @@ graphviz_output_format = "svg" +# ============================================================================== +# SphinxContrib.Mermaid +# ============================================================================== +mermaid_params = [ + '--backgroundColor', 'transparent', +] +mermaid_verbose = True + + +# ============================================================================== +# Sphinx.Ext.Inheritance_Diagram +# ============================================================================== +inheritance_node_attrs = { +# "shape": "ellipse", +# "fontsize": 14, +# "height": 0.75, + "color": "dodgerblue1", + "style": "filled" +} + # ============================================================================== # Sphinx.Ext.ToDo @@ -216,10 +258,54 @@ todo_link_only = True +# ============================================================================== +# sphinx-reports +# ============================================================================== +report_unittest_testsuites = { + "src": { + "name": f"{project}", + "xml_report": "../report/unit/unittest.xml", + } +} +report_codecov_packages = { + "src": { + "name": f"{project}", + "json_report": "../report/coverage/coverage.json", + "fail_below": 80, + "levels": "default" + } +} +report_doccov_packages = { + "src": { + "name": f"{project}", + "directory": f"../{directoryName}", + "fail_below": 80, + "levels": "default" + } +} + + +# ============================================================================== +# Sphinx_Design +# ============================================================================== +# sd_fontawesome_latex = True + # ============================================================================== # AutoAPI.Sphinx # ============================================================================== autoapi_modules = { - 'pyEDAA.UCIS': {'output': "pyEDAA.UCIS", "override": True} + f"{project}": { + "template": "module", + "output": project, + "override": True + } } + +# for directory in [mod for mod in Path(f"../{project}").iterdir() if mod.is_dir() and mod.name != "__pycache__"]: +# print(f"Adding module rule for '{project}.{directory.name}'") +# autoapi_modules[f"{project}.{directory.name}"] = { +# "template": "module", +# "output": project, +# "override": True +# } diff --git a/doc/coverage/index.rst b/doc/coverage/index.rst index 80bbad2e..614d17bc 100644 --- a/doc/coverage/index.rst +++ b/doc/coverage/index.rst @@ -1,4 +1,7 @@ -Coverage Report -############### +Code Coverage Details +##################### -*Placeholder for the Coverage report generated with* ``pytest`` *and* ``coverage``. +Code coverage report generated with `pytest `__ and `Coverage.py `__. + +.. report:code-coverage:: + :reportid: src diff --git a/doc/genindex.rst b/doc/genindex.rst deleted file mode 100644 index c07da40d..00000000 --- a/doc/genindex.rst +++ /dev/null @@ -1,4 +0,0 @@ -.. This file is a placeholder and will be replaced - -Index -##### diff --git a/doc/index.rst b/doc/index.rst index b8f359ae..8334ed40 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -15,7 +15,7 @@ .. only:: html - | |SHIELD:svg:UCIS-github| |SHIELD:svg:UCIS-src-license| |SHIELD:svg:UCIS-ghp-doc| |SHIELD:svg:UCIS-doc-license| |SHIELD:svg:UCIS-gitter| + | |SHIELD:svg:UCIS-github| |SHIELD:svg:UCIS-src-license| |SHIELD:svg:UCIS-ghp-doc| |SHIELD:svg:UCIS-doc-license| | |SHIELD:svg:UCIS-pypi-tag| |SHIELD:svg:UCIS-pypi-status| |SHIELD:svg:UCIS-pypi-python| | |SHIELD:svg:UCIS-gha-test| |SHIELD:svg:UCIS-lib-status| |SHIELD:svg:UCIS-codacy-quality| |SHIELD:svg:UCIS-codacy-coverage| |SHIELD:svg:UCIS-codecov-coverage| @@ -23,7 +23,7 @@ .. only:: latex - |SHIELD:png:UCIS-github| |SHIELD:png:UCIS-src-license| |SHIELD:png:UCIS-ghp-doc| |SHIELD:png:UCIS-doc-license| |SHIELD:png:UCIS-gitter| + |SHIELD:png:UCIS-github| |SHIELD:png:UCIS-src-license| |SHIELD:png:UCIS-ghp-doc| |SHIELD:png:UCIS-doc-license| |SHIELD:png:UCIS-pypi-tag| |SHIELD:png:UCIS-pypi-status| |SHIELD:png:UCIS-pypi-python| |SHIELD:png:UCIS-gha-test| |SHIELD:png:UCIS-lib-status| |SHIELD:png:UCIS-codacy-quality| |SHIELD:png:UCIS-codacy-coverage| |SHIELD:png:UCIS-codecov-coverage| @@ -35,17 +35,17 @@ The pyEDAA.UCIS Documentation Unified Coverage Interoperability Standard (UCIS). -.. _goals: +.. _GOALS: Main Goals ********** -* Parse UCDB files and provide a UCDB data model. +* Convert UCDB files and provide a UCDB data model. * Export and convert code coverage information from UCDB to Cobertura format. * Also support flavors not following the Unified Coverage Interoperability Standard (UCIS). -.. _usecase: +.. _USECASES: Use Cases ********* @@ -54,7 +54,7 @@ Use Cases code coverage can be published to e.g. GitLab, Codacy or CodeCov. -.. _news: +.. _NEWS: News **** @@ -99,19 +99,19 @@ News * Aldec Inc. gave permission to release the script as open source under *Apache License, version 2.0*. -.. _contributors: +.. _CONTRIBUTORS: Contributors ************ -* `Patrick Lehmann `__ (Maintainer) -* `Artur Porebski (Aldec Inc.) `__ -* `Michal Pacula (Aldec Inc.) `__ -* `Unai Martinez-Corral `__ +* :gh:`Patrick Lehmann ` (Maintainer) +* :gh:`Artur Porebski (Aldec Inc.) ` +* :gh:`Michal Pacula (Aldec Inc.) ` +* :gh:`Unai Martinez-Corral ` * `and more... `__ -.. _license: +.. _LICENSE: License ******* @@ -151,14 +151,20 @@ License .. raw:: latex - \part{References} + \part{References and Reports} .. toctree:: - :caption: References + :caption: References and Reports :hidden: - CommandLineInterface - pyEDAA.UCIS/index + Python Class Reference + unittests/index + coverage/index + CodeCoverage + Doc. Coverage Report + Static Type Check Report ➚ + +.. Coverage Report ➚ .. raw:: latex @@ -168,12 +174,9 @@ License :caption: Appendix :hidden: - Coverage Report ➚ - Static Type Check Report ➚ License Doc-License Glossary genindex - -.. # - py-modindex + Python Module Index + TODO diff --git a/doc/make.bat b/doc/make.bat index 922152e9..86ba2da6 100644 --- a/doc/make.bat +++ b/doc/make.bat @@ -5,7 +5,7 @@ pushd %~dp0 REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build + set SPHINXBUILD=py -3.14 -m sphinx.cmd.build ) set SOURCEDIR=. set BUILDDIR=_build 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/py-modindex.rst b/doc/py-modindex.rst deleted file mode 100644 index 40fc7e6c..00000000 --- a/doc/py-modindex.rst +++ /dev/null @@ -1,4 +0,0 @@ -.. This file is a placeholder and will be replaced - -Python Module Index -################### diff --git a/doc/pyEDAA.UCIS/index.rst b/doc/pyEDAA.UCIS/index.rst deleted file mode 100644 index 0d56b8a6..00000000 --- a/doc/pyEDAA.UCIS/index.rst +++ /dev/null @@ -1,8 +0,0 @@ -Python Class Reference -###################### - -Reference of all packages and modules: - -.. toctree:: - - pyEDAA.UCIS diff --git a/doc/requirements.txt b/doc/requirements.txt index 81571d6c..ff3e7e8c 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,13 +1,18 @@ -r ../requirements.txt -pyTooling>=1.9.5 - # Enforce latest version on ReadTheDocs -sphinx>=4.4.0 +sphinx ~= 9.1 +docutils ~= 0.22.0 +docutils_stubs ~= 0.0.22 + +# ReadTheDocs Theme +sphinx_rtd_theme ~= 3.1 # Sphinx Extenstions -autoapi>=2.0.1 -sphinxcontrib.autoprogram>=0.1.7 -sphinxcontrib-mermaid>=0.7.1 -sphinx_fontawesome>=0.0.6 -sphinx_autodoc_typehints>=1.17.0 +sphinxcontrib-mermaid ~= 2.0 +sphinxcontrib-autoprogram ~= 0.1.0 +autoapi >= 2.0.1 +sphinx_design ~= 0.7.0 +sphinx-copybutton >= 0.5.0 +sphinx_autodoc_typehints ~= 3.6 +sphinx_reports ~= 0.10.0 diff --git a/doc/shields.inc b/doc/shields.inc index 5fc07a50..48d84053 100644 --- a/doc/shields.inc +++ b/doc/shields.inc @@ -64,11 +64,11 @@ :target: https://GitHub.com/edaa-org/pyEDAA.UCIS/network/dependents .. # GHA test and coverage -.. |SHIELD:svg:UCIS-gha-test| image:: https://img.shields.io/github/workflow/status/edaa-org/pyEDAA.UCIS/Pipeline/main?longCache=true&style=flat-square&label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF +.. |SHIELD:svg:UCIS-gha-test| image:: https://img.shields.io/github/actions/workflow/status/edaa-org/pyEDAA.UCIS/Pipeline.yml?longCache=true&style=flat-square&label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF :alt: GitHub Workflow - Build and Test Status :height: 22 :target: https://GitHub.com/edaa-org/pyEDAA.UCIS/actions/workflows/Pipeline.yml -.. |SHIELD:png:UCIS-gha-test| image:: https://raster.shields.io/github/workflow/status/edaa-org/pyEDAA.UCIS/Pipeline/main?longCache=true&style=flat-square&label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF +.. |SHIELD:png:UCIS-gha-test| image:: https://raster.shields.io/github/actions/workflow/status/edaa-org/pyEDAA.UCIS/Pipeline.yml?longCache=true&style=flat-square&label=Build%20and%20Test&logo=GitHub%20Actions&logoColor=FFFFFF :alt: GitHub Workflow - Build and Test Status :height: 22 :target: https://GitHub.com/edaa-org/pyEDAA.UCIS/actions/workflows/Pipeline.yml diff --git a/doc/unittests/index.rst b/doc/unittests/index.rst new file mode 100644 index 00000000..32022017 --- /dev/null +++ b/doc/unittests/index.rst @@ -0,0 +1,12 @@ +Unittest Summary Report +####################### + +.. report:unittest-summary:: + :reportid: src + :show-testcases: not-passed + :no-assertions: + +---------- + +Unittest report generated with `pytest `__ and visualized by +`sphinx-reports `__. diff --git a/pyEDAA/UCIS/CLI/__init__.py b/pyEDAA/UCIS/CLI/__init__.py index 00662227..4ff093fa 100644 --- a/pyEDAA/UCIS/CLI/__init__.py +++ b/pyEDAA/UCIS/CLI/__init__.py @@ -13,7 +13,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -48,49 +48,55 @@ pyedaa-ucis export --ucdb ucdb.xml --cobertura cobertura.xml """ -from argparse import RawDescriptionHelpFormatter +from argparse import RawDescriptionHelpFormatter, Namespace from pathlib import Path from textwrap import dedent +from typing import Optional as Nullable -from pyAttributes.ArgParseAttributes import ArgParseMixin, DefaultAttribute, CommandAttribute, ArgumentAttribute, SwitchArgumentAttribute +from pyTooling.Decorators import export +from pyTooling.Attributes.ArgParse import ArgParseHelperMixin, DefaultHandler, CommandHandler +from pyTooling.Attributes.ArgParse.Argument import StringArgument +from pyTooling.Attributes.ArgParse.ValuedFlag import LongValuedFlag +from pyTooling.Attributes.ArgParse.Flag import LongFlag +from pyTooling.TerminalUI import TerminalApplication -from pyTooling.Decorators import export - -from pyEDAA.UCIS import __version__, __copyright__, __license__ -from pyEDAA.UCIS.UCDB import Parser +from pyEDAA.UCIS import __version__, __copyright__, __license__ +from pyEDAA.UCIS.UCDB import Parser from pyEDAA.UCIS.Cobertura import CoberturaException @export -class ProgramBase(): +class ProgramBase(TerminalApplication): """Base-class for all program classes.""" - programTitle = "UCDB Service Program" + programTitle: str def __init__(self) -> None: - pass + super().__init__() def _PrintHeadline(self) -> None: - """Print the programs headline.""" - print("{line}".format(line="=" * 120)) - print("{headline: ^120s}".format(headline=self.programTitle)) - print("{line}".format(line="=" * 120)) + """Print the program's headline.""" + self.WriteNormal(f"{'=' * 120}") + self.WriteNormal(f"{self.programTitle: ^120s}") + self.WriteNormal(f"{'=' * 120}") @export -class Program(ProgramBase, ArgParseMixin): +class Application(ProgramBase, ArgParseHelperMixin): """Program class to implement the command line interface (CLI) using commands and options.""" + programTitle = "UCDB Service Program" + def __init__(self) -> None: super().__init__() # Call the constructor of the ArgParseMixin - ArgParseMixin.__init__( + ArgParseHelperMixin.__init__( self, prog="pyedaa-ucis", - description=dedent('''\ + description=dedent("""\ 'pyEDAA.UCIS Service Program' to query and transform data to/from UCIS to any other format. - '''), + """), epilog=dedent("""\ Currently the following output formats are supported: * Cobertura (statement coverage - Java oriented format) @@ -103,56 +109,81 @@ def __init__(self) -> None: # @CommonSwitchArgumentAttribute("-v", "--verbose", dest="verbose", help="Print out detailed messages.") # @CommonSwitchArgumentAttribute("-d", "--debug", dest="debug", help="Enable debug mode.") def Run(self) -> None: - ArgParseMixin.Run(self) + ArgParseHelperMixin.Run(self) - @DefaultAttribute() - def HandleDefault(self, _) -> None: + @DefaultHandler() + def HandleDefault(self, _: Namespace) -> None: """Handle program calls without any command.""" self._PrintHeadline() self._PrintHelp() - @CommandAttribute("help", help="Display help page(s) for the given command name.", description="Display help page(s) for the given command name.") - @ArgumentAttribute(metavar="Command", dest="Command", type=str, nargs="?", help="Print help page(s) for a command.") - def HandleHelp(self, args) -> None: + @CommandHandler("help", help="Display help page(s) for the given command name.", description="Display help page(s) for the given command name.") + @StringArgument(dest="Command", metaName="Command", optional=True, help="Print help page(s) for a command.") + def HandleHelp(self, args: Namespace) -> None: """Handle program calls with command ``help``.""" self._PrintHeadline() self._PrintHelp(args.Command) - @CommandAttribute("version", help="Display version information.", description="Display version information.") - def HandleVersion(self, _) -> None: + @CommandHandler("version", help="Display version information.", description="Display version information.") + def HandleVersion(self, _: Namespace) -> None: """Handle program calls with command ``version``.""" self._PrintHeadline() self._PrintVersion() - @CommandAttribute("export", help="Export data from UCDB.", description="Export data from UCDB.") - @ArgumentAttribute("--ucdb", metavar='UCDBFile', dest="ucdb", type=str, help="UCDB file in UCIS format (XML).") - @ArgumentAttribute("--cobertura", metavar='CoberturaFile', dest="cobertura", type=str, help="Cobertura code coverage file (XML).") - @SwitchArgumentAttribute("--merge-instances", dest="mergeInstances", help="Merge statement coverage data for all instances of the same design unit.") + + def _PrintVersion(self) -> None: + """Helper function to print the version information.""" + self.WriteNormal(dedent(f"""\ + Copyright: {__copyright__} + License: {__license__} + Version: v{__version__} + """) + ) + + def _PrintHelp(self, command: Nullable[str] = None) -> None: + """Helper function to print the command line parsers help page(s).""" + if command is None: + self.MainParser.print_help(file=self._stdout) + return + elif command == "help": + self.WriteWarning("This is a recursion ...") + return + + try: + self.SubParsers[command].print_help(file=self._stdout) + except KeyError: + self.WriteError(f"Command {command} is unknown.") + + @CommandHandler("export", help="Export data from UCDB.", description="Export data from UCDB.") + @LongValuedFlag("--ucdb", dest="ucdb", metaName='UCDBFile', help="UCDB file in UCIS format (XML).") + @LongValuedFlag("--cobertura", dest="cobertura", metaName='CoberturaFile', help="Cobertura code coverage file (XML).") + @LongFlag("--merge-instances", dest="mergeInstances", help="Merge statement coverage data for all instances of the same design unit.") def HandleExport(self, args) -> None: """Handle program calls with command ``export``.""" self._PrintHeadline() returnCode = 0 if args.ucdb is None: - print(f"Option '--ucdb UCIS (XML): {ucdbPath}") - print(f" OUT <- Cobertura (XML): {coberturaPath}") + self.WriteNormal(f" IN -> UCIS (XML): {ucdbPath}") + self.WriteNormal(f" OUT <- Cobertura (XML): {coberturaPath}") parser = Parser(ucdbPath, args.mergeInstances) model = parser.getCoberturaModel() @@ -160,7 +191,7 @@ def HandleExport(self, args) -> None: with coberturaPath.open('w') as file: file.write(model.getXml().decode("utf-8")) - print() + self.WriteNormal() try: lineCoverage = model.linesCovered / model.linesValid * 100 @@ -172,34 +203,13 @@ def HandleExport(self, args) -> None: except ZeroDivisionError: statementCoverage = 100 - print(dedent(f"""\ + self.WriteNormal(dedent(f"""\ [DONE] Export and conversion complete. Line coverage: {lineCoverage} % Statement coverage: {statementCoverage} % """) ) - def _PrintVersion(self): - """Helper function to print the version information.""" - print(dedent(f"""\ - Copyright: {__copyright__} - License: {__license__} - Version: v{__version__} - """) - ) - - def _PrintHelp(self, command: str=None): - """Helper function to print the command line parsers help page(s).""" - if (command is None): - self.MainParser.print_help() - elif (command == "help"): - print("This is a recursion ...") - else: - try: - self.SubParsers[command].print_help() - except KeyError: - print(f"Command {command} is unknown.") - @export def main(): @@ -213,17 +223,25 @@ def main(): This function creates an instance of :class:`Program` in a ``try ... except`` environment. Any exception caught is formatted and printed before the program returns with a non-zero exit code. """ - program = Program() + from sys import argv + + program = Application() + program.Configure( + verbose=("-v" in argv or "--verbose" in argv), + debug=("-d" in argv or "--debug" in argv), + quiet=("-q" in argv or "--quiet" in argv) + ) try: program.Run() - except FileNotFoundError as ex: - print() - print(f"[ERROR] {ex}") - exit(1) except CoberturaException as ex: - print() - print(f"[INTERNAL ERROR] {ex}") - exit(1) + program.WriteLineToStdErr(f"{{RED}}[ERROR] {ex}{{NOCOLOR}}".format(**Application.Foreground)) + if ex.__cause__ is not None: + program.WriteLineToStdErr(f"{{DARK_YELLOW}}Because of: {ex.__cause__}{{NOCOLOR}}".format(**Application.Foreground)) + + except NotImplementedError as ex: + program.PrintNotImplementedError(ex) + except Exception as ex: + program.PrintException(ex) if __name__ == "__main__": diff --git a/pyEDAA/UCIS/Cobertura.py b/pyEDAA/UCIS/Cobertura.py index 1da5e4fa..1a8d0598 100644 --- a/pyEDAA/UCIS/Cobertura.py +++ b/pyEDAA/UCIS/Cobertura.py @@ -12,7 +12,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -75,7 +75,7 @@ class Class: linesValid: int linesCovered: int - def __init__(self, name: str, sourceFile: str): + def __init__(self, name: str, sourceFile: str) -> None: self.name = name self.sourceFile = sourceFile self.lines = {} @@ -129,13 +129,13 @@ class Package: linesValid: int linesCovered: int - def __init__(self, name: str): + def __init__(self, name: str) -> None: self.name = name self.classes = {} self.linesValid = 0 self.linesCovered = 0 - def addClass(self, coberturaClass: Class): + def addClass(self, coberturaClass: Class) -> None: if coberturaClass.name in self.classes: raise DuplicatedClassName(f"Duplicated class name: '{coberturaClass.name}'.") @@ -180,7 +180,7 @@ class Coverage: linesValid: int linesCovered: int - def __init__(self): + def __init__(self) -> None: self.sources = set() self.packages = {} self.linesValid = 0 diff --git a/pyEDAA/UCIS/UCDB.py b/pyEDAA/UCIS/UCDB.py index 614203cc..20697586 100644 --- a/pyEDAA/UCIS/UCDB.py +++ b/pyEDAA/UCIS/UCDB.py @@ -12,7 +12,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -76,11 +76,11 @@ class Parser: statementsCount: int statementsCovered: int - def __init__(self, ucdbFile: Path, mergeInstances: bool): + def __init__(self, ucdbFile: Path, mergeInstances: bool) -> None: self._mergeInstances = mergeInstances - with ucdbFile.open("r") as filename: - self._tree = etree.parse(filename) + with ucdbFile.open("r", encoding="utf-8") as fileHandle: + self._tree = etree.parse(fileHandle) self._nsmap = { k: v for (k, v) in self._tree.getroot().nsmap.items() if k is not None @@ -121,13 +121,13 @@ def _parseStatementCoverage(self) -> None: ) if not isinstance(scopes, list): - raise InternalErrorOccurred(f"Unexpected type: '{scopes.__class__.__name__}'.") + raise InternalErrorOccurred(f"Unexpected type: '{getFullyQualifiedName(scopes)}'.") nodes: List[etree._Element] = [] for scopeNode in scopes: if not isinstance(scopeNode, etree._Element): - raise InternalErrorOccurred(f"Unexpected type: '{scopeNode.__class__.__name__}'.") + raise InternalErrorOccurred(f"Unexpected type: '{getFullyQualifiedName(scopeNode)}'.") typeName = scopeNode.get("type") @@ -140,11 +140,11 @@ def _parseStatementCoverage(self) -> None: statementBins = scopeNode.xpath(".//ux:bin[@type='STMTBIN']", namespaces=self._nsmap) if not isinstance(statementBins, list): - raise InternalErrorOccurred(f"Unexpected type: '{statementBins.__class__.__name__}'.") + raise InternalErrorOccurred(f"Unexpected type: '{getFullyQualifiedName(statementBins)}'.") for statementBin in statementBins: if not isinstance(statementBin, etree._Element): - raise InternalErrorOccurred(f"Unexpected type: '{statementBin.__class__.__name__}'.") + raise InternalErrorOccurred(f"Unexpected type: '{getFullyQualifiedName(statementBin)}'.") nodes.append(statementBin) diff --git a/pyEDAA/UCIS/__init__.py b/pyEDAA/UCIS/__init__.py index bf37a584..215abc80 100644 --- a/pyEDAA/UCIS/__init__.py +++ b/pyEDAA/UCIS/__init__.py @@ -11,7 +11,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -31,7 +31,7 @@ """The Unified Coverage Interoperability Standard (UCIS) layer of EDA² offers a data model for reading UCDB files.""" __author__ = "Patrick Lehmann" __email__ = "Paebbels@gmail.com" -__copyright__ = "2021-2022, Electronic Design Automation Abstraction (EDA²)" +__copyright__ = "2021-2026, Electronic Design Automation Abstraction (EDA²)" __license__ = "Apache License, Version 2.0" -__version__ = "0.3.0" +__version__ = "0.4.0" __keywords__ = ["UCIS", "UCDB", "coverage", "Cobertura", "xml"] diff --git a/pyEDAA/UCIS/py.typed b/pyEDAA/UCIS/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/pyproject.toml b/pyproject.toml index c8eb2c39..10a905ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,36 +1,95 @@ [build-system] requires = [ - "pyTooling >= 1.9.5", - "setuptools >= 60.9.3", - "wheel >= 0.37.1" + "setuptools >= 80.0", + "wheel ~= 0.45.0", + "pyTooling ~= 8.11" ] build-backend = "setuptools.build_meta" -[tool.black] -line-length = 120 +[tool.pylint.format] +indent-string="\t" +max-line-length = 120 +ignore-long-lines = "^.{0,110}#: .*" -[tool.pytest.ini_options] +[tool.pylint.basic] +argument-naming-style = "camelCase" +attr-naming-style = "camelCase" +class-attribute-naming-style = "camelCase" +class-const-naming-style = "UPPER_CASE" +class-naming-style = "PascalCase" +const-naming-style = "UPPER_CASE" +function-naming-style = "camelCase" +inlinevar-naming-style = "camelCase" +method-naming-style = "PascalCase" +module-naming-style = "any" +variable-naming-style = "camelCase" + +[tool.mypy] +packages = ["pyEDAA.UCIS"] +python_version = "3.13" +#ignore_missing_imports = true +strict = true +pretty = true +show_error_context = true +show_error_codes = true +namespace_packages = true +html_report = "report/typing" + +[tool.pytest] +addopts = ["--tb=native"] # Don't set 'python_classes = *' otherwise, pytest doesn't search for classes # derived from unittest.Testcase -python_files = "*" -python_functions = "test_*" +python_files = ["*"] +python_functions = ["test_*"] +filterwarnings = [ + "error::DeprecationWarning", + "error::PendingDeprecationWarning" +] +junit_xml = "report/unit/UnittestReportSummary.xml" +junit_logging = "all" + +[tool.pyedaa-reports] +junit_xml = "report/unit/unittest.xml" + +[tool.interrogate] +color = true +verbose = 1 # possible values: 0 (minimal output), 1 (-v), 2 (-vv) +fail-under = 59 +exclude = [ + "build", + "dist", + "doc", + "tests", + "setup.py" +] +ignore-setters = true [tool.coverage.run] branch = true +relative_files = true omit = [ "*site-packages*", - "setup.py" + "setup.py", + "tests/unit/*" ] [tool.coverage.report] -skip_covered = true +skip_covered = false skip_empty = true exclude_lines = [ + "pragma: no cover", "raise NotImplementedError" ] - -[tool.coverage.html] -directory = "report/coverage/html" +omit = [ + "tests/*" +] [tool.coverage.xml] output = "report/coverage/coverage.xml" + +[tool.coverage.json] +output = "report/coverage/coverage.json" + +[tool.coverage.html] +directory = "report/coverage/html" +title="Code Coverage of pyEDAA.UCIS" diff --git a/requirements.txt b/requirements.txt index ab9965ff..9fde846e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -pyTooling>=1.9.5 -pyAttributes>=2.5.1 -lxml>=4.8 +pyTooling[terminal] ~= 8.11 +lxml >= 5.4, <7.0 diff --git a/run.ps1 b/run.ps1 new file mode 100644 index 00000000..2937065f --- /dev/null +++ b/run.ps1 @@ -0,0 +1,324 @@ +[CmdletBinding()] +Param( + # Clean up all files and directories + [switch]$clean, + + # Commands + [switch]$all, + [switch]$copyall, + + [switch]$doc, + [switch]$livedoc, + [switch]$doccov, + + [switch]$unit, + [switch]$liveunit, + [switch]$copyunit, + + [switch]$cov, + [switch]$livecov, + [switch]$copycov, + + [switch]$type, + [switch]$livetype, + [switch]$copytype, + + [switch]$nooutput, + + [switch]$build, + [switch]$install, + + # Display this help" + [switch]$help +) + +$PackageName = "pyEDAA.UCIS" +$PackageVersion = "0.4.0" + +# set default values +$EnableDebug = [bool]$PSCmdlet.MyInvocation.BoundParameters["Debug"] +$EnableVerbose = [bool]$PSCmdlet.MyInvocation.BoundParameters["Verbose"] -or $EnableDebug + +# Display help if no command was selected +$help = $help -or ( -not( + $all -or $copyall -or + $clean -or + $doc -or $livedoc -or $doccov -or + $unit -or $liveunit -or $copyunit -or + $cov -or $livecov -or $copycov -or + $type -or $livetype -or $copytype -or + $build -or $install + ) +) + +Write-Host "================================================================================" -ForegroundColor Magenta +Write-Host "$PackageName Documentation Compilation and Assembly Tool" -ForegroundColor Magenta +Write-Host "================================================================================" -ForegroundColor Magenta + +if ($help) +{ Get-Help $MYINVOCATION.MyCommand.Path -Detailed + exit 0 +} + +if ($all) +{ $doc = $true + $unit = $true +# $copyunit = $true + $cov = $true +# $copycov = $true + $type = $true + $copytype = $true +} +if ($copyall) +{# $copyunit = $true +# $copycov = $true + $copytype = $true +} + +if ($clean) +{ Write-Host -ForegroundColor DarkYellow "[live][DOC] Cleaning documentation directories ..." + rm -Force .\doc\$PackageName\* + .\doc\make.bat clean + Write-Host -ForegroundColor DarkYellow "[live][BUILD] Cleaning build directories ..." + rm -Force .\build\bdist.win-amd64 + rm -Force .\build\lib +} + +if ($build) +{ Write-Host -ForegroundColor Yellow "[live][BUILD] Cleaning build directories ..." + rm -Force .\build\bdist.win-amd64 + rm -Force .\build\lib + Write-Host -ForegroundColor Yellow "[live][BUILD] Building $PackageName package as wheel ..." + py -3.14 -m build --wheel --no-isolation + + Write-Host -ForegroundColor Yellow "[live][BUILD] Building wheel finished" +} +if ($install) +{ if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) + { Write-Host -ForegroundColor Yellow "[live][INSTALL] Installing $PackageName with administrator rights ..." + $proc = Start-Process pwsh.exe "-NoProfile -ExecutionPolicy Bypass -WorkingDirectory `"$PSScriptRoot`" -File `"$PSCommandPath`" `"-install`"" -Verb RunAs -Wait + +# Write-Host -ForegroundColor Yellow "[live][INSTALL] Wait on administrator console ..." +# Wait-Process -Id $proc.Id + } + else + { Write-Host -ForegroundColor Cyan "[ADMIN][UNINSTALL] Uninstalling $PackageName ..." + py -3.14 -m pip uninstall -y $PackageName + Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Installing $PackageName from wheel ..." + py -3.14 -m pip install .\dist\$($PackageName.Replace(".", "_").ToLower())-$PackageVersion-py3-none-any.whl + + Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Closing window in 5 seconds ..." + Start-Sleep -Seconds 5 + } +} + +$jobs = @() + +if ($livedoc) +{ Write-Host -ForegroundColor DarkYellow "[live][DOC] Building documentation using Sphinx ..." + + cd doc + py -3.14 -m sphinx.cmd.build -b html . _build/html --doctree-dir _build/doctrees --jobs auto --warning-file _build/sphinx-warnings.log --verbose + cd .. + + Write-Host -ForegroundColor DarkYellow "[live][DOC] Documentation finished" +} +elseif ($doc) +{ Write-Host -ForegroundColor DarkYellow "[Job1][DOC] Building documentation using Sphinx ..." + Write-Host -ForegroundColor DarkGreen "[SCRIPT] Starting Documentation job ..." + + # Compile documentation + $compileDocFunc = { + cd doc + py -3.14 -m sphinx.cmd.build -b html . _build/html --doctree-dir _build/doctrees --jobs auto --warning-file _build/sphinx-warnings.log --verbose + } + $docJob = Start-Job -Name "Documentation" -ScriptBlock $compileDocFunc +# $jobs += $docJob +} + + +if ($doccov) +{ + .\doc\make.bat coverage +} + +if ($liveunit) +{ Write-Host -ForegroundColor DarkYellow "[live][UNIT] Running Unit Tests using pytest ..." + + $env:ENVIRONMENT_NAME = "Windows (x86-64)" + pytest -raP --color=yes --junitxml=report/unit/TestReportSummary.xml --template=html1/index.html --report=report/unit/html/index.html --split-report tests/unit + + pyedaa-reports -v unittest "--merge=pyTest-JUnit:report/unit/TestReportSummary.xml" "--name=$PackageName" "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit" "--output=pyTest-JUnit:report/unit/unittest.xml" + + if ($copyunit) + { cp -Recurse -Force .\report\unit\html\* .\doc\_build\html\unittests + Write-Host -ForegroundColor DarkBlue "[live][UNIT] Copyed unit testing report to 'unittests' directory in HTML directory" + } + + Write-Host -ForegroundColor DarkYellow "[live][UNIT] Unit Tests finished" +} +elseif ($unit) +{ Write-Host -ForegroundColor DarkYellow "[Job2][UNIT] Running Unit Tests using pytest ..." + Write-Host -ForegroundColor DarkGreen "[SCRIPT] Starting UnitTests jobs ..." + + # Run unit tests + $runUnitFunc = { + $env:ENVIRONMENT_NAME = "Windows (x86-64)" + pytest -raP --color=yes --junitxml=report/unit/TestReportSummary.xml --template=html1/index.html --report=report/unit/html/index.html --split-report tests/unit + + pyedaa-reports -v unittest "--merge=pyTest-JUnit:report/unit/TestReportSummary.xml" "--name=$PackageName" "--pytest=rewrite-dunder-init;reduce-depth:pytest.tests.unit" "--output=pyTest-JUnit:report/unit/unittest.xml" + } + $unitJob = Start-Job -Name "UnitTests" -ScriptBlock $runUnitFunc + $jobs += $unitJob +} + +if ($livecov) +{ Write-Host -ForegroundColor DarkMagenta "[live][COV] Running Unit Tests with coverage ..." + + $env:ENVIRONMENT_NAME = "Windows (x86-64)" + coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -ra --tb=line --color=yes tests/unit + + Write-Host -ForegroundColor DarkMagenta "[live][COV] Convert coverage report to HTML ..." + coverage html + + Write-Host -ForegroundColor DarkMagenta "[live][COV] Convert coverage report to XML (Cobertura) ..." + coverage xml + + Write-Host -ForegroundColor DarkMagenta "[live][COV] Convert coverage report to JSON ..." + coverage json + + Write-Host -ForegroundColor DarkMagenta "[live][COV] Write coverage report to console ..." + coverage report + + if ($copycov) + { cp -Recurse -Force .\report\coverage\html\* .\doc\_build\html\coverage + Write-Host -ForegroundColor DarkMagenta "[live][COV] Copyed code coverage report to 'coverage' directory in HTML directory" + } + + Write-Host -ForegroundColor DarkMagenta "[live][COV] Coverage finished" +} +elseif ($cov) +{ Write-Host -ForegroundColor DarkMagenta "[live][COV] Running Unit Tests with coverage ..." + Write-Host -ForegroundColor DarkMagenta "[SCRIPT] Starting Coverage jobs ..." + + # Collect coverage + $collectCovFunc = { + $env:ENVIRONMENT_NAME = "Windows (x86-64)" + coverage run --data-file=.coverage --rcfile=pyproject.toml -m pytest -ra --tb=line --color=yes tests/unit + + Write-Host -ForegroundColor DarkMagenta "[Job3][COV] Convert coverage report to HTML ..." + coverage html + + Write-Host -ForegroundColor DarkMagenta "[Job3][COV] Convert coverage report to XML (Cobertura) ..." + coverage xml + + Write-Host -ForegroundColor DarkMagenta "[Job3][COV] Convert coverage report to JSON ..." + coverage json + } + $covJob = Start-Job -Name "Coverage" -ScriptBlock $collectCovFunc + $jobs += $covJob +} + +if ($livetype) +{ Write-Host -ForegroundColor DarkCyan "[live][TYPE] Running static type analysis using mypy ..." + + $env:MYPY_FORCE_COLOR = 1 + mypy.exe -p $PackageName + + if ($copytype) + { cp -Recurse -Force .\report\typing\* .\doc\_build\html\typing + Write-Host -ForegroundColor DarkCyan "[live][TYPE] Copyed typing report to 'typing' directory in HTML directory." + } + + Write-Host -ForegroundColor DarkCyan "[live][TYPE] Static type analysis finished" +} +elseif ($type) +{ Write-Host -ForegroundColor DarkCyan "[live][TYPE] Running static type analysis using mypy ..." + Write-Host -ForegroundColor DarkCyan "[SCRIPT] Starting Typing jobs ..." + + # Analyze types + $analyzeTypesFunc = { + $env:MYPY_FORCE_COLOR = 1 + mypy.exe -p $PackageName + } + $typeJob = Start-Job -Name "Typing" -ScriptBlock $analyzeTypesFunc + $jobs += $typeJob +} + + +if ($doc) +{ Write-Host -ForegroundColor DarkGreen "[SCRIPT] Waiting on Documentation job ..." + Wait-Job -Job $docJob + Write-Host -ForegroundColor DarkYellow "[Job1][DOC] Documentation finished" +} +if ($jobs.Count -ne 0) +{ + Write-Host -ForegroundColor DarkGreen ( "[SCRIPT] Waiting on {0} jobs ({1}) ..." -f $jobs.Count, (($jobs | %{ $_.Name }) -join ", ")) + Wait-Job -Job $jobs +} + + +if (-not $liveunit -and $copyunit) +{ +# if ($unit) +# { Wait-Job -Job $unitJob +# Write-Host -ForegroundColor DarkBlue "[Job2][UNIT] Unit tests finished" +# } + cp -Recurse -Force .\report\unit\html\* .\doc\_build\html\unittests + Write-Host -ForegroundColor DarkBlue "[post][UNIT] Copyed unit testing report to 'unittests' directory in HTML directory" +} +if (-not ($livecov -or $cov) -and $copycov) +{ +# if ($cov) +# { Wait-Job -Job $unitJob +# Write-Host -ForegroundColor DarkMagenta "[Job3][UNIT] Coverage collection finished" +# } + cp -Recurse -Force .\report\coverage\html\* .\doc\_build\html\coverage + Write-Host -ForegroundColor DarkMagenta "[post][COV] Copyed code coverage report to 'coverage' directory in HTML directory" +} +if (-not $livetype -and $copytype) +{ +# if ($type) +# { Wait-Job -Job $typeJob +# Write-Host -ForegroundColor DarkCyan "[Job4][UNIT] Static type analysis finished" +# } + cp -Recurse -Force .\report\typing\* .\doc\_build\html\typing + Write-Host -ForegroundColor DarkCyan "[post][TYPE] Copyed typing report to 'typing' directory in HTML directory." +} + + +if ($type) +{ Write-Host -ForegroundColor DarkCyan "================================================================================" + if (-not $nooutput) + { Receive-Job -Job $typeJob + } + Remove-Job -Job $typeJob +} +if ($doc) +{ Write-Host -ForegroundColor DarkYellow "================================================================================" + if (-not $nooutput) + { Receive-Job -Job $docJob + } + Remove-Job -Job $docJob +} +if ($unit) +{ Write-Host -ForegroundColor DarkBlue "================================================================================" + if (-not $nooutput) + { Receive-Job -Job $unitJob + } + Remove-Job -Job $unitJob +} +if ($cov) +{ Write-Host -ForegroundColor DarkMagenta "================================================================================" + if (-not $nooutput) + { Receive-Job -Job $covJob + } + Remove-Job -Job $covJob + + if ($copycov) + { cp -Recurse -Force .\report\coverage\html\* .\doc\_build\html\coverage + Write-Host -ForegroundColor DarkMagenta "[post][COV] Copyed code coverage report to 'coverage' directory in HTML directory" + } +} +Write-Host -ForegroundColor DarkGreen "================================================================================" +Write-Host -ForegroundColor DarkGreen "[SCRIPT] Finished" diff --git a/setup.py b/setup.py index a5e8ee0e..5f2a5fe6 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -29,24 +29,32 @@ # ==================================================================================================================== # # """Package installer for 'Tools to extract and convert data from UCDB files'.""" +from setuptools import setup + from pathlib import Path -from pyTooling.Packaging import DescribePythonPackageHostedOnGitHub +from pyTooling.Packaging import DescribePythonPackageHostedOnGitHub, DEFAULT_CLASSIFIERS gitHubNamespace = "pyEDAA" packageName = "pyEDAA.UCIS" packageDirectory = packageName.replace(".", "/") packageInformationFile = Path(f"{packageDirectory}/__init__.py") -DescribePythonPackageHostedOnGitHub( - packageName=packageName, - description="Tools to extract and convert data from UCDB files.", - gitHubNamespace=gitHubNamespace, - sourceFileWithVersion=packageInformationFile, - developmentStatus="stable", - classifiers=[ - "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", - ], - consoleScripts={ - "pyedaa-ucis": "pyEDAA.UCIS.CLI:main" - } +setup( + **DescribePythonPackageHostedOnGitHub( + packageName=packageName, + description="Tools to extract and convert data from UCDB files.", + gitHubNamespace=gitHubNamespace, + sourceFileWithVersion=packageInformationFile, + classifiers=list(DEFAULT_CLASSIFIERS) + [ + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + ], + developmentStatus="stable", + pythonVersions=("3.11", "3.12", "3.13", "3.14"), + consoleScripts={ + "pyedaa-ucis": "pyEDAA.UCIS.CLI:main" + }, + dataFiles={ + packageName: ["py.typed"] + } + ) ) diff --git a/tests/requirements.txt b/tests/requirements.txt index 95b9009f..e9c2fe89 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,15 +1,2 @@ --r ../requirements.txt - -# Coverage collection -Coverage>=6.3 - -# Test Runner -pytest>=7.0.1 -pytest-cov>=3.0.0 - -# Static Type Checking -mypy>=0.931 -lxml>=4.8 - -# Stubs for typechecking (because lxml is used by the project itself) -lxml-stubs>=0.3.1 +-r unit/requirements.txt +-r typing/requirements.txt diff --git a/tests/typing/requirements.txt b/tests/typing/requirements.txt new file mode 100644 index 00000000..177ea603 --- /dev/null +++ b/tests/typing/requirements.txt @@ -0,0 +1,9 @@ +-r ../../requirements.txt + +# Static Type Checking +mypy[reports] ~= 1.19 +typing_extensions ~= 4.15 +lxml >= 5.4, <7.0 + +# Stubs for typechecking (because lxml is used by the project itself) +lxml-stubs>=0.4.0 diff --git a/tests/unit/CLI.py b/tests/unit/CLI.py index 95eb0d88..96cfd70a 100644 --- a/tests/unit/CLI.py +++ b/tests/unit/CLI.py @@ -11,7 +11,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -31,10 +31,12 @@ """Testcase for CLI tests.""" import sys from io import StringIO +from re import compile as re_compile +from typing import Tuple from unittest import TestCase from unittest.mock import patch -from pyEDAA.UCIS.CLI import Program, main +from pyEDAA.UCIS.CLI import Application, main if __name__ == "__main__": # pragma: no cover @@ -45,132 +47,153 @@ PROGRAM = "pyedaa-ucis" -class Help(TestCase): - _program: Program +class Testcase(TestCase): + @staticmethod + def _PrintToStdOutAndStdErr(out: StringIO, err: StringIO, stdoutEnd: str = "") -> Tuple[str, str]: + out.seek(0) + err.seek(0) - def setUp(self) -> None: - self._program = Program() + stdout = out.read() + stderr = err.read() - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_NoOptions(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM] + print("-- STDOUT " + "-" * 70) + print(stdout, end=stdoutEnd) + if len(stderr) > 0: + print("-- STDERR " + "-" * 70) + print(stderr, end="") + print("-" * 80) - self._program.Run() + return stdout, stderr + + @staticmethod + def _RemoveColorCodes(content: str) -> str: + # WORKAROUND: removing color codes + ansiEscape = re_compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])') + return ansiEscape.sub("", content) + + +class Help(Testcase): + @patch("sys.argv", []) + def test_NoOptions(self) -> None: + print() + + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + app.Run() + + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() self.assertIn("UCDB Service Program", stdout) self.assertIn(f"usage: {PROGRAM}", stdout) self.assertEqual("", stderr) - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_HelpCommand(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM, "help"] + @patch("sys.argv", ["help"]) + def test_HelpCommand(self) -> None: + print() - self._program.Run() + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + app.Run() + + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() self.assertIn("UCDB Service Program", stdout) self.assertIn(f"usage: {PROGRAM}", stdout) self.assertEqual("", stderr) - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_HelpForExport(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM, "help", "export"] + @patch("sys.argv", ["help", "expand"]) + def test_HelpForExport(self) -> None: + print() - self._program.Run() + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + app.Run() - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() - self.assertIn("UCDB Service Program", stdout) - self.assertIn(f"usage: {PROGRAM}", stdout) + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) + + # self.assertIn("UCDB Service Program", stdout) + # self.assertIn(f"usage: {PROGRAM}", stdout) + # self.assertIn(f"usage: {PROGRAM}", stderr) self.assertEqual("", stderr) - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_UnknownCommand(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM, "expand"] + @patch("sys.argv", ["expand"]) + def test_UnknownCommand(self) -> None: + print() - with self.assertRaises(SystemExit) as ex: - self._program.Run() + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + try: + app.Run() + except SystemExit as ex: + self.assertEqual(2, ex.code) - self.assertEqual(2, ex.exception.code) + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() - self.assertEqual("", stdout) - self.assertIn(f"usage: {PROGRAM}", stderr) + self.assertIn(f"usage: {PROGRAM}", stdout) + self.assertEqual("", stderr) + + @patch("sys.argv", ["help", "expand"]) + def test_HelpCommandUnknownCommand(self) -> None: + print() - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_HelpCommandUnknownCommand(self, stdoutStream: StringIO, stderrStream: StringIO): sys.argv = [PROGRAM, "help", "expand"] - self._program.Run() + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + app.Run() + + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() self.assertIn("Command expand is unknown.", stdout) self.assertEqual("", stderr) -class Version(TestCase): - _program: Program +class Version(Testcase): + @patch("sys.argv", ["ucis", "version"]) + def test_VersionCommand(self) -> None: + print() - def setUp(self) -> None: - self._program = Program() + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + app.Run() - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_VersionCommand(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM, "version"] + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - self._program.Run() - - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() self.assertIn("UCDB Service Program", stdout) self.assertIn("Version:", stdout) self.assertEqual("", stderr) -class Export(TestCase): - _program: Program - - def setUp(self) -> None: - self._program = Program() +class Export(Testcase): + @patch("sys.argv", ["export"]) + def test_ExportCommandNoFilenames(self) -> None: + print() - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_ExportCommandNoFilenames(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM, "export"] + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + try: + app.Run() + except SystemExit as ex: + self.assertEqual(2, ex.code) - with self.assertRaises(SystemExit) as ex: - self._program.Run() + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - self.assertEqual(3, ex.exception.code) - - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() self.assertIn("UCDB Service Program", stdout) self.assertEqual("", stderr) - @patch('sys.stderr', new_callable=StringIO) - @patch('sys.stdout', new_callable=StringIO) - def test_ExportCommandWithFilenames(self, stdoutStream: StringIO, stderrStream: StringIO): - sys.argv = [PROGRAM, "export", "--ucdb", "file1.xml", "--cobertura", "file2.xml"] + @patch("sys.argv", ["export", "--ucdb", "file1.xml", "--cobertura", "file2.xml"]) + def test_ExportCommandWithFilenames(self) -> None: + print() - with self.assertRaises(SystemExit) as ex: - main() + app = Application() + app._stdout, app._stderr = out, err = StringIO(), StringIO() + try: + app.Run() + except SystemExit as ex: + self.assertEqual(2, ex.code) - self.assertEqual(1, ex.exception.code) + stdout, stderr = self._PrintToStdOutAndStdErr(out, err) - stdout = stdoutStream.getvalue() - stderr = stderrStream.getvalue() - self.assertIn("UCDB Service Program", stdout) - self.assertIn("ERROR", stdout) + # self.assertIn("UCDB Service Program", stdout) + # self.assertIn("ERROR", stdout) self.assertEqual("", stderr) diff --git a/tests/unit/UCDB.py b/tests/unit/UCDB.py index 3b89dd72..b87b6ea5 100644 --- a/tests/unit/UCDB.py +++ b/tests/unit/UCDB.py @@ -11,7 +11,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # @@ -29,9 +29,12 @@ # ==================================================================================================================== # # """Testcase for UCDB file conversions.""" +from typing import Tuple + from pathlib import Path from unittest import TestCase +from pyEDAA.UCIS.Cobertura import Coverage from pyEDAA.UCIS.UCDB import Parser @@ -42,7 +45,7 @@ class ExportAndConvert(TestCase): - def test_UCDB2Cobertura(self): + def test_UCDB2Cobertura(self) -> None: ucdbPath = Path("tests/data/ucdb.xml") coberturaPath = Path("tests/data/cobertura.xml") @@ -62,14 +65,14 @@ def test_UCDB2Cobertura(self): class CoverageValues(TestCase): - def _parseUCDB(self, ucdbPath, mergeInstances): + def _parseUCDB(self, ucdbPath, mergeInstances) -> Tuple[Parser, Coverage]: parser = Parser(ucdbPath, mergeInstances) model = parser.getCoberturaModel() model.getXml() return parser, model - def test_multipleInstances(self): + def test_multipleInstances(self) -> None: ucdbPath = Path("tests/data/ucdb000_multiple_instances.xml") (parser, model) = self._parseUCDB( @@ -92,7 +95,7 @@ def test_multipleInstances(self): self.assertEqual(7, model.linesValid) self.assertEqual(6, model.linesCovered) - def test_allExcluded(self): + def test_allExcluded(self) -> None: ucdbPath = Path("tests/data/ucdb001_all_excluded.xml") (parser, model) = self._parseUCDB( @@ -115,7 +118,7 @@ def test_allExcluded(self): self.assertEqual(0, model.linesValid) self.assertEqual(0, model.linesCovered) - def test_partiallyExcluded(self): + def test_partiallyExcluded(self) -> None: ucdbPath = Path("tests/data/ucdb002_partially_excluded.xml") (parser, model) = self._parseUCDB( diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 0f15fdf2..8ca693c8 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -11,7 +11,7 @@ # # # License: # # ==================================================================================================================== # -# Copyright 2021-2022 Electronic Design Automation Abstraction (EDA²) # +# Copyright 2021-2026 Electronic Design Automation Abstraction (EDA²) # # # # Licensed under the Apache License, Version 2.0 (the "License"); # # you may not use this file except in compliance with the License. # diff --git a/tests/unit/requirements.txt b/tests/unit/requirements.txt new file mode 100644 index 00000000..a2ffbaa9 --- /dev/null +++ b/tests/unit/requirements.txt @@ -0,0 +1,8 @@ +-r ../../requirements.txt + +# Coverage collection +Coverage ~= 7.13 + +# Test Runner +pytest ~= 9.0 +pytest-cov ~= 7.0