From 7dcf9e57c34c50ddd11f87c51e417cb1afc332f3 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Sat, 12 Oct 2024 18:21:58 +0200 Subject: [PATCH 1/2] Allow all Sphinx 8.x versions. Bumped version to v0.7.2. --- doc/requirements.txt | 2 +- requirements.txt | 4 ++-- run.ps1 | 2 +- sphinx_reports/__init__.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index c1c7f3a4..8bfaa985 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -6,7 +6,7 @@ setuptools ~= 75.1 pyTooling ~= 6.7 # Enforce latest version on ReadTheDocs -sphinx ~= 8.0, <8.1 +sphinx ~= 8.1 docutils ~= 0.21 # ReadTheDocs Theme diff --git a/requirements.txt b/requirements.txt index f0f1c1d4..9fd574e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pyTooling ~= 6.7 -pyEDAA.Reports ~= 0.12.1 +pyEDAA.Reports ~= 0.13 -sphinx ~= 8.0, <8.1 +sphinx ~= 8.0 docutils ~= 0.21 Coverage ~= 7.6 diff --git a/run.ps1 b/run.ps1 index 661e036f..ccb7cdca 100644 --- a/run.ps1 +++ b/run.ps1 @@ -97,7 +97,7 @@ if ($install) { Write-Host -ForegroundColor Cyan "[ADMIN][UNINSTALL] Uninstalling $PackageName ..." py -3.12 -m pip uninstall -y $PackageName Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Installing $PackageName from wheel ..." - py -3.12 -m pip install .\dist\$PackageName-0.7.1-py3-none-any.whl + py -3.12 -m pip install .\dist\$PackageName-0.8.0-py3-none-any.whl Write-Host -ForegroundColor Cyan "[ADMIN][INSTALL] Closing window in 5 seconds ..." Start-Sleep -Seconds 5 diff --git a/sphinx_reports/__init__.py b/sphinx_reports/__init__.py index f9e924ef..bea99b9f 100644 --- a/sphinx_reports/__init__.py +++ b/sphinx_reports/__init__.py @@ -43,7 +43,7 @@ __email__ = "Paebbels@gmail.com" __copyright__ = "2023-2024, Patrick Lehmann" __license__ = "Apache License, Version 2.0" -__version__ = "0.7.1" +__version__ = "0.7.2" __keywords__ = ["Python3", "Sphinx", "Extension", "Report", "doc-string", "interrogate"] from hashlib import md5 From f94bbfcc969e09e4cf7148b14ffbbfce6b18de79 Mon Sep 17 00:00:00 2001 From: Patrick Lehmann Date: Sun, 13 Oct 2024 15:25:00 +0200 Subject: [PATCH 2/2] Improved error handling if configuration is missing in conf.py or configuration was invalid. --- doc/Doc-License.rst | 2 +- sphinx_reports/CodeCoverage.py | 57 +++++++++++++++++++++++++++------- sphinx_reports/DocCoverage.py | 18 ++++++++--- sphinx_reports/Sphinx.py | 9 ++++++ sphinx_reports/Unittest.py | 27 ++++++++-------- sphinx_reports/__init__.py | 18 +++++++++-- 6 files changed, 98 insertions(+), 33 deletions(-) diff --git a/doc/Doc-License.rst b/doc/Doc-License.rst index c2595bde..ca0c2560 100644 --- a/doc/Doc-License.rst +++ b/doc/Doc-License.rst @@ -17,7 +17,7 @@ licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. -.. rubric:: Using Creative Commons Public Licenses +.. topic:: Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of diff --git a/sphinx_reports/CodeCoverage.py b/sphinx_reports/CodeCoverage.py index c47f3c4c..5ec6daca 100644 --- a/sphinx_reports/CodeCoverage.py +++ b/sphinx_reports/CodeCoverage.py @@ -271,7 +271,11 @@ def _CheckOptions(self) -> None: self._noBranchCoverage = "no-branch-coverage" in self.options - packageConfiguration = self._packageConfigurations[self._packageID] + try: + packageConfiguration = self._packageConfigurations[self._packageID] + except KeyError as ex: + raise ReportExtensionError(f"No configuration for '{self._packageID}'") from ex + self._packageName = packageConfiguration["name"] self._jsonReport = packageConfiguration["json_report"] self._failBelow = packageConfiguration["fail_below"] @@ -372,11 +376,11 @@ def _CreatePages(self) -> None: def handlePackage(package: PackageCoverage) -> None: for pack in package._packages.values(): if handlePackage(pack): - return True + return for module in package._modules.values(): if handleModule(module): - return True + return def handleModule(module: ModuleCoverage) -> None: doc = new_document("dummy") @@ -392,12 +396,18 @@ def handleModule(module: ModuleCoverage) -> None: self.env.titles[docname] = title self.env.longtitles[docname] = title - return True + return handlePackage(self._coverage) def run(self) -> List[nodes.Node]: - self._CheckOptions() + container = nodes.container() + + try: + self._CheckOptions() + except ReportExtensionError as ex: + message = f"Caught {ex.__class__.__name__} when checking options for directive '{self.directiveName}'." + return self._internalError(container, __name__, message, ex) # Assemble a list of Python source files analyzer = Analyzer(self._packageName, self._jsonReport) @@ -406,7 +416,6 @@ def run(self) -> List[nodes.Node]: self._CreatePages() - container = nodes.container() container += self._GenerateCoverageTable() docName = self.env.docname @@ -481,7 +490,11 @@ def _CheckOptions(self) -> None: self._style = self._ParseLegendStyle("style", LegendStyle.horizontal_table) - packageConfiguration = self._packageConfigurations[self._packageID] + try: + packageConfiguration = self._packageConfigurations[self._packageID] + except KeyError as ex: + raise ReportExtensionError(f"No configuration for '{self._packageID}'") from ex + self._levels = packageConfiguration["levels"] def _CreateHorizontalLegendTable(self, identifier: str, classes: List[str]) -> nodes.table: @@ -534,9 +547,14 @@ def _CreateVerticalLegendTable(self, identifier: str, classes: List[str]) -> nod return table def run(self) -> List[nodes.Node]: - self._CheckOptions() - container = nodes.container() + + try: + self._CheckOptions() + except ReportExtensionError as ex: + message = f"Caught {ex.__class__.__name__} when checking options for directive '{self.directiveName}'." + return self._internalError(container, __name__, message, ex) + if LegendStyle.Table in self._style: if LegendStyle.Horizontal in self._style: container += self._CreateHorizontalLegendTable(identifier=f"{self._packageID}-legend", classes=["report-codecov-legend"]) @@ -578,12 +596,22 @@ def _CheckOptions(self) -> None: self._moduleName = self._ParseStringOption("module") - packageConfiguration = self._packageConfigurations[self._packageID] + try: + packageConfiguration = self._packageConfigurations[self._packageID] + except KeyError as ex: + raise ReportExtensionError(f"No configuration for '{self._packageID}'") from ex + self._packageName = packageConfiguration["name"] self._jsonReport = packageConfiguration["json_report"] def run(self) -> List[nodes.Node]: - self._CheckOptions() + container = nodes.container() + + try: + self._CheckOptions() + except ReportExtensionError as ex: + message = f"Caught {ex.__class__.__name__} when checking options for directive '{self.directiveName}'." + return self._internalError(container, __name__, message, ex) # Assemble a list of Python source files analyzer = Analyzer(self._packageName, self._jsonReport) @@ -591,9 +619,14 @@ def run(self) -> List[nodes.Node]: sourceFile = "../../sphinx_reports/__init__.py" - container = nodes.container() container += nodes.paragraph(text=f"Code coverage of {self._moduleName}") + # lexer = get_lexer_by_name("python", tabsize=2) + # tokens = lex(code, lexer) + + # htmlFormatter = HtmlFormatter(linenos=True, cssclass="source") + # highlight() + location = self.state_machine.get_source_and_line(self.lineno) rel_filename, filename = self.env.relfn2path(sourceFile) self.env.note_dependency(rel_filename) diff --git a/sphinx_reports/DocCoverage.py b/sphinx_reports/DocCoverage.py index f0b79df6..882b9ddb 100644 --- a/sphinx_reports/DocCoverage.py +++ b/sphinx_reports/DocCoverage.py @@ -330,7 +330,13 @@ def renderlevel(tableBody: nodes.tbody, packageCoverage: PackageCoverage, level: @export class DocStrCoverage(DocCoverage): def run(self) -> List[nodes.Node]: - self._CheckOptions() + container = nodes.container() + + try: + self._CheckOptions() + except ReportExtensionError as ex: + message = f"Caught {ex.__class__.__name__} when checking options for directive '{self.directiveName}'." + return self._internalError(container, __name__, message, ex) # Assemble a list of Python source files docStrCov = DocStrCovAnalyzer(self._packageName, self._directory) @@ -339,7 +345,6 @@ def run(self) -> List[nodes.Node]: # self._coverage.CalculateCoverage() self._coverage.Aggregate() - container = nodes.container() container += self._GenerateCoverageTable() return [container] @@ -414,9 +419,14 @@ def _CreateVerticalLegendTable(self, identifier: str, classes: List[str]) -> nod return table def run(self) -> List[nodes.Node]: - self._CheckOptions() - container = nodes.container() + + try: + self._CheckOptions() + except ReportExtensionError as ex: + message = f"Caught {ex.__class__.__name__} when checking options for directive '{self.directiveName}'." + return self._internalError(container, __name__, message, ex) + if LegendStyle.Table in self._style: if LegendStyle.Horizontal in self._style: container += self._CreateHorizontalLegendTable(identifier=f"{self._packageID}-legend", classes=["report-doccov-legend"]) diff --git a/sphinx_reports/Sphinx.py b/sphinx_reports/Sphinx.py index eb81d9a1..2505f096 100644 --- a/sphinx_reports/Sphinx.py +++ b/sphinx_reports/Sphinx.py @@ -37,6 +37,7 @@ from docutils import nodes from sphinx.directives import ObjectDescription from pyTooling.Decorators import export +from sphinx.util.logging import getLogger from sphinx_reports.Common import ReportExtensionError, LegendStyle @@ -174,3 +175,11 @@ def _CreateTableHeader(self, columns: List[Tuple[str, Nullable[List[Tuple[str, i tableHeader += tableRow return table, tableGroup + + def _internalError(self, container: nodes.container, location: str, message: str, exception: Exception) -> List[nodes.Node]: + logger = getLogger(location) + logger.error(f"{message}\n {exception}") + + container += nodes.paragraph(text=message) + + return [container] diff --git a/sphinx_reports/Unittest.py b/sphinx_reports/Unittest.py index d295603c..283bcf55 100644 --- a/sphinx_reports/Unittest.py +++ b/sphinx_reports/Unittest.py @@ -42,7 +42,6 @@ from pyEDAA.Reports.Unittesting.JUnit import Testsuite, TestsuiteSummary, Testcase, Document from sphinx.application import Sphinx from sphinx.config import Config -from sphinx.util.logging import getLogger from sphinx_reports.Common import ReportExtensionError from sphinx_reports.Sphinx import strip, BaseDirective @@ -245,33 +244,35 @@ def renderTestcase(tableBody: nodes.tbody, testcase: Testcase, level: int) -> No return table def run(self) -> List[nodes.Node]: - self._CheckOptions() + container = nodes.container() + + try: + self._CheckOptions() + except ReportExtensionError as ex: + message = f"Caught {ex.__class__.__name__} when checking options for directive '{self.directiveName}'." + return self._internalError(container, __name__, message, ex) # Assemble a list of Python source files try: - doc = Document(self._xmlReport, parse=True) + doc = Document(self._xmlReport, analyzeAndConvert=True) except Exception as ex: - logger = getLogger(__name__) - logger.error(f"Caught {ex.__class__.__name__} when reading and parsing '{self._xmlReport}'.\n {ex}") - return [] + message = f"Caught {ex.__class__.__name__} when reading and parsing '{self._xmlReport}'." + return self._internalError(container, __name__, message, ex) doc.Aggregate() try: self._testsuite = doc.ToTestsuiteSummary() except Exception as ex: - logger = getLogger(__name__) - logger.error(f"Caught {ex.__class__.__name__} when converting to a TestsuiteSummary for JUnit document '{self._xmlReport}'.\n {ex}") - return [] + message = f"Caught {ex.__class__.__name__} when converting to a TestsuiteSummary for JUnit document '{self._xmlReport}'." + return self._internalError(container, __name__, message, ex) self._testsuite.Aggregate() try: - container = nodes.container() container += self._GenerateTestSummaryTable() except Exception as ex: - logger = getLogger(__name__) - logger.error(f"Caught {ex.__class__.__name__} when generating the document structure for JUnit document '{self._xmlReport}'.\n {ex}") - return [] + message = f"Caught {ex.__class__.__name__} when generating the document structure for JUnit document '{self._xmlReport}'." + return self._internalError(container, __name__, message, ex) return [container] diff --git a/sphinx_reports/__init__.py b/sphinx_reports/__init__.py index bea99b9f..6ad2ff87 100644 --- a/sphinx_reports/__init__.py +++ b/sphinx_reports/__init__.py @@ -59,6 +59,9 @@ from sphinx.environment import BuildEnvironment from pyTooling.Decorators import export from pyTooling.Common import readResourceFile +from sphinx.util.logging import getLogger + +from sphinx_reports.Common import ReportExtensionError from sphinx_reports import static as ResourcePackage @@ -173,9 +176,18 @@ def CheckConfigurationVariables(sphinxApplication: Sphinx, config: Config) -> No from sphinx_reports.DocCoverage import DocCoverageBase from sphinx_reports.Unittest import UnittestSummary - CodeCoverageBase.CheckConfiguration(sphinxApplication, config) - DocCoverageBase.CheckConfiguration(sphinxApplication, config) - UnittestSummary.CheckConfiguration(sphinxApplication, config) + checkConfigurations = ( + CodeCoverageBase.CheckConfiguration, + DocCoverageBase.CheckConfiguration, + UnittestSummary.CheckConfiguration, + ) + + for checkConfiguration in checkConfigurations: + try: + checkConfiguration(sphinxApplication, config) + except ReportExtensionError as ex: + logger = getLogger(__name__) + logger.error(f"Caught {ex.__class__.__name__} when checking configuration variables.\n {ex}") @staticmethod def AddCSSFiles(sphinxApplication: Sphinx) -> None: