Coverage for sphinx_reports/__init__.py: 54%
97 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-09 22:12 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-09 22:12 +0000
1# ==================================================================================================================== #
2# _ _ _ #
3# ___ _ __ | |__ (_)_ __ __ __ _ __ ___ _ __ ___ _ __| |_ ___ #
4# / __| '_ \| '_ \| | '_ \\ \/ /____| '__/ _ \ '_ \ / _ \| '__| __/ __| #
5# \__ \ |_) | | | | | | | |> <_____| | | __/ |_) | (_) | | | |_\__ \ #
6# |___/ .__/|_| |_|_|_| |_/_/\_\ |_| \___| .__/ \___/|_| \__|___/ #
7# |_| |_| #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2023-2025 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32**A Sphinx domain providing directives to add reports to the Sphinx-based documentation.**
34Supported reports:
36* :ref:`UNITTEST`
37* :ref:`DOCCOV`
38* :ref:`CODECOV`
39* :ref:`DEP`
41"""
42__author__ = "Patrick Lehmann"
43__email__ = "Paebbels@gmail.com"
44__copyright__ = "2023-2025, Patrick Lehmann"
45__license__ = "Apache License, Version 2.0"
46__version__ = "0.9.4"
47__keywords__ = [
48 "Python3", "Sphinx", "Extension", "Report", "doc-string", "interrogate", "Code Coverage", "Coverage",
49 "Documentation Coverage", "Unittest", "Dependencies", "Summary"
50]
52from hashlib import md5
53from pathlib import Path
54from typing import TYPE_CHECKING, Any, Tuple, Dict, Optional as Nullable, TypedDict, List, Callable
56from docutils import nodes
57from sphinx.addnodes import pending_xref
58from sphinx.application import Sphinx
59from sphinx.builders import Builder
60from sphinx.config import Config
61from sphinx.domains import Domain
62from sphinx.environment import BuildEnvironment
63from sphinx.util.logging import getLogger
64from pyTooling.Decorators import export
65from pyTooling.Common import readResourceFile
67from sphinx_reports import static as ResourcePackage
68from sphinx_reports.Common import ReportExtensionError
71@export
72class ReportDomain(Domain):
73 """
74 A Sphinx extension providing a ``report`` domain to integrate reports and summaries into a Sphinx-based documentation.
76 .. rubric:: New directives:
78 * :rst:dir:`report:code-coverage`
79 * :rst:dir:`report:code-coverage-legend`
80 * :rst:dir:`report:doc-coverage`
81 * :rst:dir:`report:doc-coverage-legend`
82 * :rst:dir:`report:dependency-table`
83 * :rst:dir:`report:unittest-summary`
85 .. rubric:: New roles:
87 * *None*
89 .. rubric:: New indices:
91 * *None*
93 .. rubric:: Configuration variables
95 All configuration variables in :file:`conf.py` are prefixed with ``report_*``:
97 * ``report_codecov_packages``
98 * ``report_doccov_packages``
99 * ``report_unittest_testsuites``
101 """
103 name = "report" #: The name of this domain
104 label = "rpt" #: The label of this domain
106 dependencies: List[str] = [
107 ] #: A list of other extensions this domain depends on.
109 from sphinx_reports.CodeCoverage import CodeCoverage, CodeCoverageLegend, ModuleCoverage
110 from sphinx_reports.DocCoverage import DocStrCoverage, DocCoverageLegend
111 from sphinx_reports.Dependency import DependencyTable
112 from sphinx_reports.Unittest import UnittestSummary
114 directives = {
115 "code-coverage": CodeCoverage,
116 "code-coverage-legend": CodeCoverageLegend,
117 "module-coverage": ModuleCoverage,
118 "doc-coverage": DocStrCoverage,
119 "doc-coverage-legend": DocCoverageLegend,
120 "dependency-table": DependencyTable,
121 "unittest-summary": UnittestSummary,
122 } #: A dictionary of all directives in this domain.
124 roles = {
125 # "design": DesignRole,
126 } #: A dictionary of all roles in this domain.
128 indices = [
129 # LibraryIndex,
130 ] #: A list of all indices in this domain.
132 from sphinx_reports.CodeCoverage import CodeCoverageBase
133 from sphinx_reports.DocCoverage import DocCoverageBase
134 from sphinx_reports.Dependency import DependencyTable
135 from sphinx_reports.Unittest import UnittestSummary
137 configValues: Dict[str, Tuple[Any, str, Any]] = {
138 **CodeCoverageBase.configValues,
139 **DocCoverageBase.configValues,
140 **UnittestSummary.configValues,
141 **DependencyTable.configValues,
142 } #: A dictionary of all configuration values used by this domain. (name: (default, rebuilt, type))
144 del CodeCoverageBase
145 del CodeCoverage
146 del CodeCoverageLegend
147 del ModuleCoverage
148 del DocCoverageBase
149 del DocStrCoverage
150 del DocCoverageLegend
151 del DependencyTable
152 del UnittestSummary
154 initial_data = {
155 # "reports": {}
156 } #: A dictionary of all global data fields used by this domain.
158 # @property
159 # def Reports(self) -> Dict[str, Any]:
160 # return self.data["reports"]
162 @staticmethod
163 def CheckConfigurationVariables(sphinxApplication: Sphinx, config: Config) -> None:
164 """
165 Call back for Sphinx ``config-inited`` event.
167 This callback will verify configuration variables used by that domain.
169 .. seealso::
171 Sphinx *builder-inited* event
172 See https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events
174 :param sphinxApplication: The Sphinx application.
175 :param config: Sphinx configuration parsed from ``conf.py``.
176 """
177 from sphinx_reports.CodeCoverage import CodeCoverageBase
178 from sphinx_reports.DocCoverage import DocCoverageBase
179 from sphinx_reports.Unittest import UnittestSummary
181 checkConfigurations = (
182 CodeCoverageBase.CheckConfiguration,
183 DocCoverageBase.CheckConfiguration,
184 UnittestSummary.CheckConfiguration,
185 )
187 for checkConfiguration in checkConfigurations:
188 try:
189 checkConfiguration(sphinxApplication, config)
190 except ReportExtensionError as ex:
191 logger = getLogger(__name__)
192 logger.error(f"Caught {ex.__class__.__name__} when checking configuration variables.\n {ex}")
194 @staticmethod
195 def AddCSSFiles(sphinxApplication: Sphinx) -> None:
196 """
197 Call back for Sphinx ``builder-inited`` event.
199 This callback will copy the CSS file(s) to the build directory.
201 .. seealso::
203 Sphinx *builder-inited* event
204 See https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events
206 :param sphinxApplication: The Sphinx application.
207 """
208 # Create a new static path for this extension
209 staticDirectory = (Path(sphinxApplication.outdir) / "_report_static").resolve()
210 staticDirectory.mkdir(exist_ok=True)
211 sphinxApplication.config.html_static_path.append(str(staticDirectory))
213 # Read the CSS content from package resources and hash it
214 cssFilename = "sphinx-reports.css"
215 cssContent = readResourceFile(ResourcePackage, cssFilename)
217 # Compute md5 hash of CSS file
218 hash = md5(cssContent.encode("utf8")).hexdigest()
220 # Write the CSS file into output directory
221 cssFile = staticDirectory / f"sphinx-reports.{hash}.css"
222 sphinxApplication.add_css_file(cssFile.name)
224 if not cssFile.exists():
225 # Purge old CSS files
226 for file in staticDirectory.glob("*.css"):
227 file.unlink()
229 # Write CSS content
230 cssFile.write_text(cssContent, encoding="utf8")
232 @staticmethod
233 def ReadReports(sphinxApplication: Sphinx) -> None:
234 """
235 Call back for Sphinx ``builder-inited`` event.
237 This callback will read the linked report files
239 .. seealso::
241 Sphinx *builder-inited* event
242 See https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events
244 :param sphinxApplication: The Sphinx application.
245 """
246 from sphinx_reports.CodeCoverage import CodeCoverageBase
247 from sphinx_reports.Unittest import UnittestSummary
249 CodeCoverageBase.ReadReports(sphinxApplication)
250 UnittestSummary.ReadReports(sphinxApplication)
252 callbacks: Dict[str, List[Callable]] = {
253 "config-inited": [CheckConfigurationVariables], # (app, config)
254 "builder-inited": [AddCSSFiles, ReadReports], # (app)
255 } #: A dictionary of all events/callbacks <https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events>`__ used by this domain.
257 def resolve_xref(
258 self,
259 env: BuildEnvironment,
260 fromdocname: str,
261 builder: Builder,
262 typ: str,
263 target: str,
264 node: pending_xref,
265 contnode: nodes.Element
266 ) -> Nullable[nodes.Element]:
267 raise NotImplementedError()
270if TYPE_CHECKING: 270 ↛ 271line 270 didn't jump to line 271 because the condition on line 270 was never true
271 class setup_ReturnType(TypedDict):
272 version: str
273 env_version: int
274 parallel_read_safe: bool
275 parallel_write_safe: bool
278@export
279def setup(sphinxApplication: Sphinx) -> "setup_ReturnType":
280 """
281 Extension setup function registering the ``report`` domain in Sphinx.
283 It will execute these steps:
285 * register domains, directives and roles.
286 * connect events (register callbacks)
287 * register configuration variables for :file:`conf.py`
289 :param sphinxApplication: The Sphinx application.
290 :return: Dictionary containing the extension version and some properties.
291 """
292 sphinxApplication.add_domain(ReportDomain)
294 # Register callbacks
295 for eventName, callbacks in ReportDomain.callbacks.items():
296 for callback in callbacks:
297 sphinxApplication.connect(eventName, callback)
299 # Register configuration options supported/needed in Sphinx's 'conf.py'
300 for configName, (configDefault, configRebuilt, configTypes) in ReportDomain.configValues.items():
301 sphinxApplication.add_config_value(f"{ReportDomain.name}_{configName}", configDefault, configRebuilt, configTypes)
303 return {
304 "version": __version__, # version of the extension
305 "env_version": int(__version__.split(".")[0]), # version of the data structure stored in the environment
306 'parallel_read_safe': False, # Not yet evaluated, thus false
307 'parallel_write_safe': True, # Internal data structure is used read-only, thus no problems will occur by parallel writing.
308 }