From bd3746ac49f7c3d0ea39c2d5dc2fa74c140f2c47 Mon Sep 17 00:00:00 2001 From: Maximilian Pollak Date: Thu, 28 Nov 2024 17:59:45 +0100 Subject: [PATCH] Upgraded - Upgraded to allow for compaitiblity with Sphinx-needs 4.0+ - Changed paths to allow for better integration with other tools (like bazel). - Added setuptools as dependency as it wasn't there already but is used. - Added possibility for mutliple values in 'tr_link' targeted option - Enabled TestFileDirective, TestSuiteDirective, TestCaseDirective to have additional options from extra_links & extra_options - Edited tests to only test newer versions of Sphinx & Python - Fixed tests to account for new paths --- docs/conf.py | 4 +- noxfile.py | 34 ++++++------- setup.py | 10 ++-- sphinxcontrib/__init__.py | 1 - sphinxcontrib/test_reports/__init__.py | 2 +- .../test_reports/directives/test_case.py | 46 ++++++++++++++--- .../test_reports/directives/test_common.py | 47 ++++++++++++++--- .../test_reports/directives/test_file.py | 46 +++++++++++++---- .../test_reports/directives/test_report.py | 4 +- .../test_reports/directives/test_results.py | 2 +- .../test_reports/directives/test_suite.py | 50 +++++++++++++++---- .../test_reports/functions/__init__.py | 15 +++--- sphinxcontrib/test_reports/test_reports.py | 33 ++++++++---- tests/doc_test/basic_doc/conf.py | 1 - tests/doc_test/doc_test_file/conf.py | 2 +- tests/doc_test/doc_test_file/index.rst | 1 + tests/test_custom_template.py | 2 +- 17 files changed, 220 insertions(+), 80 deletions(-) delete mode 100644 sphinxcontrib/__init__.py diff --git a/docs/conf.py b/docs/conf.py index 70dffdb..bc3bdcc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,9 +29,9 @@ author = "team useblocks" # The short X.Y version -version = "1.0" +version = "1.1" # The full version, including alpha/beta/rc tags -release = "1.0.2" +release = "1.1.0" # -- General configuration --------------------------------------------------- diff --git a/noxfile.py b/noxfile.py index bb0b81f..4a5b5e1 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,8 +1,8 @@ import nox from nox import session -PYTHON_VERSIONS = ["3.8", "3.9", "3.10"] -SPHINX_VERSIONS = ["5.0", "6.2.1", "7.1.2", "7.2.5"] +PYTHON_VERSIONS = ["3.11", "3.12"] +SPHINX_VERSIONS = ["7.4.1", "8.1"] TEST_DEPENDENCIES = [ "pytest", "pytest-xdist", @@ -36,18 +36,18 @@ def tests(session, sphinx): session.skip("unsupported combination") -@session(python="3.9") -def lint(session): - session.install(*LINT_DEPENDENCIES) - session.run("make", "lint", external=True) - - -@session(python="3.9") -def linkcheck(session): - session.install(".") - # LinkCheck cn handle rate limits since Sphinx 3.4, which is needed as - # our doc has to many links to GitHub. - session.run("pip", "install", "sphinx==3.5.4", silent=True) - - session.run("pip", "install", "-r", "doc-requirements.txt", silent=True) - session.run("make", "docs-linkcheck", external=True) +# @session(python="3.9") +# def lint(session): +# session.install(*LINT_DEPENDENCIES) +# session.run("make", "lint", external=True) +# +# +# @session(python="3.11") +# def linkcheck(session): +# session.install(".") +# # LinkCheck cn handle rate limits since Sphinx 3.4, which is needed as +# # our doc has to many links to GitHub. +# session.run("pip", "install", "sphinx==3.5.4", silent=True) +# +# session.run("pip", "install", "-r", "doc-requirements.txt", silent=True) +# session.run("make", "docs-linkcheck", external=True) diff --git a/setup.py b/setup.py index d8f585b..1c75e43 100644 --- a/setup.py +++ b/setup.py @@ -2,15 +2,15 @@ import os -from setuptools import find_packages, setup +from setuptools import find_namespace_packages, setup -requires = ["sphinx>=4.0", "lxml", "sphinx-needs>=1.0.1"] +requires = ["sphinx>=7.4.1", "lxml", "sphinx-needs>=4.0.0", "setuptools"] with open(os.path.join(os.path.dirname(__file__), "README.rst")) as file: setup( name="sphinx-test-reports", # Update also test_reports.py, conf.py and changelog! - version="1.0.2", + version="1.1.0a", url="http://github.com/useblocks/sphinx-test-reports", download_url="http://pypi.python.org/pypi/sphinx-test-reports", license="MIT", @@ -33,10 +33,12 @@ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Documentation", ], platforms="any", - packages=find_packages(), + packages=find_namespace_packages(include=['sphinxcontrib.*']), include_package_data=True, install_requires=requires, namespace_packages=["sphinxcontrib"], diff --git a/sphinxcontrib/__init__.py b/sphinxcontrib/__init__.py deleted file mode 100644 index 1288267..0000000 --- a/sphinxcontrib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__import__("pkg_resources").declare_namespace(__name__) # noqa: F401 diff --git a/sphinxcontrib/test_reports/__init__.py b/sphinxcontrib/test_reports/__init__.py index f546f90..539cede 100644 --- a/sphinxcontrib/test_reports/__init__.py +++ b/sphinxcontrib/test_reports/__init__.py @@ -1,2 +1,2 @@ # from sphinxcontrib.test_reports.needs.dyn_functions import Results4Needs -from sphinxcontrib.test_reports.test_reports import setup +from .test_reports import setup diff --git a/sphinxcontrib/test_reports/directives/test_case.py b/sphinxcontrib/test_reports/directives/test_case.py index ab8b1c9..783796b 100644 --- a/sphinxcontrib/test_reports/directives/test_case.py +++ b/sphinxcontrib/test_reports/directives/test_case.py @@ -2,9 +2,10 @@ from docutils.parsers.rst import directives from sphinx_needs.api import add_need from sphinx_needs.utils import add_doc +from sphinx_needs.config import NeedsSphinxConfig -from sphinxcontrib.test_reports.directives.test_common import TestCommonDirective -from sphinxcontrib.test_reports.exceptions import TestReportInvalidOption +from .test_common import TestCommonDirective +from ..exceptions import TestReportInvalidOption class TestCase(nodes.General, nodes.Element): @@ -33,9 +34,6 @@ class TestCaseDirective(TestCommonDirective): final_argument_whitespace = True - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def run(self, nested=False, suite_count=-1, case_count=-1): self.prepare_basic_options() self.load_test_file() @@ -163,6 +161,41 @@ def run(self, nested=False, suite_count=-1, case_count=-1): main_section = [] docname = self.state.document.settings.env.docname + needs_config = NeedsSphinxConfig(self.env.config) + extra_links = needs_config.extra_links + extra_options = needs_config.extra_options + specified_opts = ( + "docname", + "lineno", + "type", + "title", + "id", + "content", + "links", + "tags", + "status", + "collapse", + "file", + "suite", + "case", + "case_name", + "case_parameter", + "classname", + "result", + "time", + "style", + "passed", + "skipped", + "failed", + "errors", + ) + need_extra_options = {} + extra_links_options = [x["option"] for x in extra_links] + all_options = extra_links_options + list(extra_options.keys()) + for extra_option in all_options: + if extra_option not in specified_opts: + need_extra_options[extra_option] = self.options.get(extra_option, "") + main_section += add_need( self.app, self.state, @@ -185,7 +218,8 @@ def run(self, nested=False, suite_count=-1, case_count=-1): result=result, time=time, style=style, + **need_extra_options ) - + add_doc(self.env, docname) return main_section diff --git a/sphinxcontrib/test_reports/directives/test_common.py b/sphinxcontrib/test_reports/directives/test_common.py index 68c4af3..7d52cbd 100644 --- a/sphinxcontrib/test_reports/directives/test_common.py +++ b/sphinxcontrib/test_reports/directives/test_common.py @@ -4,14 +4,17 @@ # fmt: off import os -from docutils.parsers.rst import Directive +from docutils.parsers.rst import Directive, directives from sphinx.util import logging -from sphinx_needs.api import make_hashed_id +from sphinx_needs.api.need import _make_hashed_id +from sphinx_needs.config import NeedsSphinxConfig +from sphinx_needs.data import SphinxNeedsData, NeedsInfoType +from typing import Any, Dict, List, Iterable -from sphinxcontrib.test_reports.exceptions import ( +from ..exceptions import ( SphinxError, TestReportFileNotSetException) -from sphinxcontrib.test_reports.jsonparser import JsonParser -from sphinxcontrib.test_reports.junitparser import JUnitParser +from ..jsonparser import JsonParser +from ..junitparser import JUnitParser # fmt: on @@ -21,6 +24,36 @@ class TestCommonDirective(Directive): Common directive, which provides some shared functions to "real" directives. """ + @classmethod + def update_option_spec(cls, app): + """ + Adding extra_options & extra_links defined via sphinx_needs to 'allowed' options + """ + needs_config = NeedsSphinxConfig(app.config) + + if not hasattr(cls, 'option_spec'): + cls.option_spec = {} + elif cls.option_spec is None: + cls.option_spec = {} + new_options = dict(getattr(cls, 'option_spec', {}) or {}) + + extra_options = getattr(needs_config, 'extra_options', None) + if extra_options is None: + extra_options = {} + extra_links = getattr(needs_config, 'extra_links', None) + if extra_links is None: + extra_links = {} + + extra_links_options = [x["option"] for x in extra_links] + all_options = extra_links_options + list(extra_options.keys()) + + for option in all_options: + if option not in new_options: + new_options[option] = directives.unchanged + + cls.option_spec = new_options + + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.env = self.state.document.settings.env @@ -90,9 +123,7 @@ def prepare_basic_options(self): self.need_type = self.app.tr_types[self.name][0] self.test_id = self.options.get( "id", - make_hashed_id( - self.app, self.need_type, self.test_name, self.test_content - ), + _make_hashed_id(self.need_type, self.test_name, self.test_content, NeedsSphinxConfig(self.app.config)) ) else: self.test_id = self.options.get("id") diff --git a/sphinxcontrib/test_reports/directives/test_file.py b/sphinxcontrib/test_reports/directives/test_file.py index b2c6b59..3e5bdce 100644 --- a/sphinxcontrib/test_reports/directives/test_file.py +++ b/sphinxcontrib/test_reports/directives/test_file.py @@ -4,10 +4,12 @@ from docutils.parsers.rst import directives from sphinx_needs.api import add_need from sphinx_needs.utils import add_doc +from sphinx_needs.config import NeedsSphinxConfig -import sphinxcontrib.test_reports.directives.test_suite -from sphinxcontrib.test_reports.directives.test_common import TestCommonDirective -from sphinxcontrib.test_reports.exceptions import TestReportIncompleteConfiguration +from sphinx_needs.data import NeedsInfoType +from .test_suite import TestSuiteDirective +from .test_common import TestCommonDirective +from ..exceptions import TestReportIncompleteConfiguration class TestFile(nodes.General, nodes.Element): @@ -35,13 +37,38 @@ class TestFileDirective(TestCommonDirective): final_argument_whitespace = True - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.suite_ids = {} - def run(self): + self.suite_ids = {} self.prepare_basic_options() results = self.load_test_file() + needs_config = NeedsSphinxConfig(self.env.config) + extra_links = needs_config.extra_links + extra_options = needs_config.extra_options + specified_opts = ( + "docname", + "lineno", + "type", + "title", + "id", + "content", + "links", + "tags", + "status", + "collapse", + "file", + "suites", + "cases", + "passed", + "skipped", + "failed", + "errors", + ) + need_extra_options = {} + extra_links_options = [x["option"] for x in extra_links] + all_options = extra_links_options + list(extra_options.keys()) + for extra_option in all_options: + if extra_option not in specified_opts: + need_extra_options[extra_option] = self.options.get(extra_option, "") # Error handling, if file not found if results is None: @@ -84,7 +111,8 @@ def run(self): passed=passed, skipped=skipped, failed=failed, - errors=errors, + errors=errors, + **need_extra_options ) if ( @@ -124,7 +152,7 @@ def run(self): arguments = [suite["name"]] suite_directive = ( - sphinxcontrib.test_reports.directives.test_suite.TestSuiteDirective( + TestSuiteDirective( self.app.config.tr_suite[0], arguments, options, diff --git a/sphinxcontrib/test_reports/directives/test_report.py b/sphinxcontrib/test_reports/directives/test_report.py index 53c8065..288376c 100644 --- a/sphinxcontrib/test_reports/directives/test_report.py +++ b/sphinxcontrib/test_reports/directives/test_report.py @@ -4,9 +4,9 @@ from docutils import nodes from docutils.parsers.rst import directives -from sphinxcontrib.test_reports.directives.test_common import \ +from .test_common import \ TestCommonDirective -from sphinxcontrib.test_reports.exceptions import InvalidConfigurationError +from ..exceptions import InvalidConfigurationError # fmt: on diff --git a/sphinxcontrib/test_reports/directives/test_results.py b/sphinxcontrib/test_reports/directives/test_results.py index 46fc8fe..9cc2f6e 100644 --- a/sphinxcontrib/test_reports/directives/test_results.py +++ b/sphinxcontrib/test_reports/directives/test_results.py @@ -3,7 +3,7 @@ from docutils import nodes from docutils.parsers.rst import Directive -from sphinxcontrib.test_reports.junitparser import JUnitParser +from ..junitparser import JUnitParser class TestResults(nodes.General, nodes.Element): diff --git a/sphinxcontrib/test_reports/directives/test_suite.py b/sphinxcontrib/test_reports/directives/test_suite.py index 0d30da7..dc252f8 100644 --- a/sphinxcontrib/test_reports/directives/test_suite.py +++ b/sphinxcontrib/test_reports/directives/test_suite.py @@ -5,9 +5,13 @@ from sphinx_needs.api import add_need from sphinx_needs.utils import add_doc -import sphinxcontrib.test_reports.directives.test_case -from sphinxcontrib.test_reports.directives.test_common import TestCommonDirective -from sphinxcontrib.test_reports.exceptions import TestReportInvalidOption + + +from sphinx_needs.config import NeedsSphinxConfig +from sphinx_needs.data import NeedsInfoType +from .test_case import TestCaseDirective +from .test_common import TestCommonDirective +from ..exceptions import TestReportInvalidOption class TestSuite(nodes.General, nodes.Element): @@ -34,11 +38,8 @@ class TestSuiteDirective(TestCommonDirective): final_argument_whitespace = True - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def run(self, nested=False, count=-1, **kwargs): self.case_ids = [] - - def run(self, nested=False, count=-1): self.prepare_basic_options() self.load_test_file() @@ -75,6 +76,34 @@ def run(self, nested=False, count=-1): main_section = [] docname = self.state.document.settings.env.docname + needs_config = NeedsSphinxConfig(self.env.config) + extra_links = needs_config.extra_links + extra_options = needs_config.extra_options + specified_opts = ( + "docname", + "lineno", + "type", + "title", + "id", + "content", + "links", + "tags", + "status", + "collapse", + "file", + "suite", + "cases", + "passed", + "skipped", + "failed", + "errors", + ) + need_extra_options = {} + extra_links_options = [x["option"] for x in extra_links] + all_options = extra_links_options + list(extra_options.keys()) + for extra_option in all_options: + if extra_option not in specified_opts: + need_extra_options[extra_option] = self.options.get(extra_option, "") main_section += add_need( self.app, self.state, @@ -94,7 +123,8 @@ def run(self, nested=False, count=-1): passed=passed, skipped=skipped, failed=failed, - errors=errors, + errors=errors, + **need_extra_options ) # TODO double nested logic @@ -121,7 +151,7 @@ def run(self, nested=False, count=-1): arguments = [suite["name"]] suite_directive = ( - sphinxcontrib.test_reports.directives.test_suite.TestSuiteDirective( + directives.test_suite.TestSuiteDirective( self.app.config.tr_suite[0], arguments, options, @@ -176,7 +206,7 @@ def run(self, nested=False, count=-1): arguments = [case["name"]] case_directive = ( - sphinxcontrib.test_reports.directives.test_case.TestCaseDirective( + TestCaseDirective( self.app.config.tr_case[0], arguments, options, diff --git a/sphinxcontrib/test_reports/functions/__init__.py b/sphinxcontrib/test_reports/functions/__init__.py index 6373ab3..978850e 100644 --- a/sphinxcontrib/test_reports/functions/__init__.py +++ b/sphinxcontrib/test_reports/functions/__init__.py @@ -1,16 +1,19 @@ def tr_link(app, need, needs, test_option, target_option, *args, **kwargs): if test_option not in need: return "" - test_opt = need[test_option] + + # Allow for multiple values in option + test_opt_values = need[test_option].split(",") links = [] for need_target in needs.values(): if target_option not in need_target: continue - - if ( - test_opt == need_target[target_option] and test_opt is not None and len(test_opt) > 0 # fmt: skip - ): - links.append(need_target["id"]) + for test_opt_raw in test_opt_values: + test_opt = test_opt_raw.strip() + if ( + test_opt == need_target[target_option] and test_opt is not None and len(test_opt) > 0 # fmt: skip + ): + links.append(need_target["id"]) return links diff --git a/sphinxcontrib/test_reports/test_reports.py b/sphinxcontrib/test_reports/test_reports.py index 118c464..51d1f5e 100644 --- a/sphinxcontrib/test_reports/test_reports.py +++ b/sphinxcontrib/test_reports/test_reports.py @@ -3,24 +3,24 @@ import sphinx from packaging.version import Version -# from docutils import nodes from sphinx_needs.api import (add_dynamic_function, add_extra_option, add_need_type) -from sphinxcontrib.test_reports.directives.test_case import (TestCase, +from .directives.test_case import (TestCase, TestCaseDirective) -from sphinxcontrib.test_reports.directives.test_env import (EnvReport, +from .directives.test_env import (EnvReport, EnvReportDirective) -from sphinxcontrib.test_reports.directives.test_file import (TestFile, +from .directives.test_file import (TestFile, TestFileDirective) -from sphinxcontrib.test_reports.directives.test_report import ( +from .directives.test_common import TestCommonDirective +from .directives.test_report import ( TestReport, TestReportDirective) -from sphinxcontrib.test_reports.directives.test_results import ( +from .directives.test_results import ( TestResults, TestResultsDirective) -from sphinxcontrib.test_reports.directives.test_suite import ( +from .directives.test_suite import ( TestSuite, TestSuiteDirective) -from sphinxcontrib.test_reports.environment import install_styles_static_files -from sphinxcontrib.test_reports.functions import tr_link +from .environment import install_styles_static_files +from .functions import tr_link sphinx_version = sphinx.__version__ if Version(sphinx_version) >= Version("1.6"): @@ -30,7 +30,7 @@ # fmt: on -VERSION = "1.0.2" +VERSION = "1.1.0a" def setup(app): @@ -117,6 +117,7 @@ def setup(app): app.connect("env-updated", install_styles_static_files) app.connect("config-inited", tr_preparation) app.connect("config-inited", sphinx_needs_update) + app.connect("env-before-read-docs", add_extra_options_to_directives) return { "version": VERSION, # identifies the version of our extension @@ -142,6 +143,7 @@ def tr_preparation(app, *args): app.add_directive(app.config.tr_case[0], TestCaseDirective) + def sphinx_needs_update(app, *args): """ sphinx-needs configuration @@ -179,3 +181,14 @@ def sphinx_needs_update(app, *args): add_need_type(app, *app.config.tr_file[1:]) add_need_type(app, *app.config.tr_suite[1:]) add_need_type(app, *app.config.tr_case[1:]) + +def add_extra_options_to_directives(app, env, *args, **kwargs): + """ + Add 'needs_extra_options' to the 'opt_spec' of the directives. + In order to allow them to have said directives inside rst files + """ + if not hasattr(env.config, 'needs_extra_options'): + env.config.needs_extra_options = [] + TestCaseDirective.update_option_spec(app) + TestSuiteDirective.update_option_spec(app) + TestFileDirective.update_option_spec(app) diff --git a/tests/doc_test/basic_doc/conf.py b/tests/doc_test/basic_doc/conf.py index a69b1cb..a18062b 100644 --- a/tests/doc_test/basic_doc/conf.py +++ b/tests/doc_test/basic_doc/conf.py @@ -64,7 +64,6 @@ # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = "en" - # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path diff --git a/tests/doc_test/doc_test_file/conf.py b/tests/doc_test/doc_test_file/conf.py index 0bd2db7..b675979 100644 --- a/tests/doc_test/doc_test_file/conf.py +++ b/tests/doc_test/doc_test_file/conf.py @@ -75,7 +75,7 @@ # The master toctree document. master_doc = "index" -needs_extra_options = ["asil", "uses_secure"] +needs_extra_options = ["asil", "uses_secure", "verifies"] # General information about the project. project = "test-report test docs" diff --git a/tests/doc_test/doc_test_file/index.rst b/tests/doc_test/doc_test_file/index.rst index 8a02869..3afd234 100644 --- a/tests/doc_test/doc_test_file/index.rst +++ b/tests/doc_test/doc_test_file/index.rst @@ -23,6 +23,7 @@ Basic Document FOR TEST FILE .. test-file:: My Test Data :file: ../utils/xml_data.xml + :verifies: needs_extra_option :id: TESTFILE_1 diff --git a/tests/test_custom_template.py b/tests/test_custom_template.py index dd1a273..ac8b4d6 100644 --- a/tests/test_custom_template.py +++ b/tests/test_custom_template.py @@ -8,7 +8,7 @@ [{"buildername": "html", "srcdir": "doc_test/custom_tr_template"}], indirect=True, ) -def test_custom_template(test_app): +def test_custom_template(test_app, capsys): app = test_app app.build() html = Path(app.outdir / "index.html").read_text()