Skip to content

Commit

Permalink
Improved typehints and fixed mypy reported errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
Paebbels committed Jan 10, 2024
1 parent 33a1a66 commit 235317d
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 63 deletions.
6 changes: 3 additions & 3 deletions sphinx_reports/Adapter/DocStrCoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from pyTooling.Decorators import export, readonly

from sphinx_reports.Common import ReportExtensionError
from sphinx_reports.DataModel.DocumentationCoverage import ModuleCoverage, PackageCoverage
from sphinx_reports.DataModel.DocumentationCoverage import ModuleCoverage, PackageCoverage, AggregatedCoverage


@export
Expand All @@ -54,7 +54,7 @@ class Analyzer:
_moduleFiles: List[Path]
_coverageReport: str

def __init__(self, directory: Path, packageName: str):
def __init__(self, directory: Path, packageName: str) -> None:
self._searchDirectory = directory
self._packageName = packageName
self._moduleFiles = []
Expand Down Expand Up @@ -95,7 +95,7 @@ def Convert(self) -> PackageCoverage:
moduleName = path.stem
modulePath = [p.name for p in path.parents]

currentCoverageObject = rootPackageCoverage
currentCoverageObject: AggregatedCoverage = rootPackageCoverage
for packageName in modulePath[1:]:
try:
currentCoverageObject = currentCoverageObject[packageName]
Expand Down
25 changes: 16 additions & 9 deletions sphinx_reports/CodeCoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,24 @@
**Report code coverage as Sphinx documentation page(s).**
"""
from pathlib import Path
from typing import Dict, Tuple, Any, List, Iterable, Mapping, Generator
from typing import Dict, Tuple, Any, List, Iterable, Mapping, Generator, TypedDict

from docutils import nodes
from pyTooling.Decorators import export

from sphinx_reports.Common import ReportExtensionError
from sphinx_reports.Sphinx import strip, LegendPosition, BaseDirective
from sphinx_reports.DataModel.DocumentationCoverage import PackageCoverage
from sphinx_reports.DataModel.DocumentationCoverage import PackageCoverage, AggregatedCoverage
from sphinx_reports.Adapter.DocStrCoverage import Analyzer


class package_DictType(TypedDict):
name: str
directory: str
fail_below: int
levels: Dict[int, Dict[str, str]]


@export
class CodeCoverage(BaseDirective):
"""
Expand Down Expand Up @@ -71,15 +78,15 @@ class CodeCoverage(BaseDirective):
_levels: Dict[int, Dict[str, str]]
_coverage: PackageCoverage

def _CheckOptions(self):
def _CheckOptions(self) -> None:
# Parse all directive options or use default values
self._packageID = self._ParseStringOption("packageid")
self._legend = self._ParseLegendOption("legend", LegendPosition.Bottom)

def _CheckConfiguration(self):
def _CheckConfiguration(self) -> None:
# Check configuration fields and load necessary values
try:
allPackages = self.config[f"{self.configPrefix}_packages"]
allPackages: Dict[str, package_DictType] = self.config[f"{self.configPrefix}_packages"]
except (KeyError, AttributeError) as ex:
raise ReportExtensionError(f"Configuration option '{self.configPrefix}_packages' is not configured.") from ex

Expand Down Expand Up @@ -118,7 +125,7 @@ def _CheckConfiguration(self):
100: {"class": "doccov-below100", "background": "rgba( 0, 200, 82, .2)", "desc": "excellent documented"},
}

def _ConvertToColor(self, currentLevel, configKey):
def _ConvertToColor(self, currentLevel: float, configKey: str) -> str:
for levelLimit, levelConfig in self._levels.items():
if (currentLevel * 100) < levelLimit:
return levelConfig[configKey]
Expand All @@ -141,11 +148,11 @@ def _GenerateCoverageTable(self) -> nodes.table:
tableBody = nodes.tbody()
tableGroup += tableBody

def sortedValues(d: Mapping) -> Generator[Any, None, None]:
def sortedValues(d: Mapping[str, AggregatedCoverage]) -> Generator[AggregatedCoverage, None, None]:
for key in sorted(d.keys()):
yield d[key]

def renderlevel(tableBody: nodes.tbody, packageCoverage: PackageCoverage, level: int = 0):
def renderlevel(tableBody: nodes.tbody, packageCoverage: PackageCoverage, level: int = 0) -> None:
tableBody += nodes.row(
"",
nodes.entry("", nodes.paragraph(text=f"{' '*level}{packageCoverage.Name} ({packageCoverage.File})")),
Expand Down Expand Up @@ -217,7 +224,7 @@ def _CreateLegend(self, identifier: str, classes: Iterable[str]) -> List[nodes.E

return [rubric, table]

def run(self):
def run(self) -> List[nodes.Node]:
self._CheckOptions()
self._CheckConfiguration()

Expand Down
2 changes: 1 addition & 1 deletion sphinx_reports/Common.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ReportExtensionError(ExtensionError):
# Implementing a dummy method for Python versions before
__notes__: List[str]
if version_info < (3, 11): # pragma: no cover
def add_note(self, message: str):
def add_note(self, message: str) -> None:
try:
self.__notes__.append(message)
except AttributeError:
Expand Down
40 changes: 17 additions & 23 deletions sphinx_reports/DataModel/DocumentationCoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

from enum import Flag
from pathlib import Path
from typing import Optional as Nullable, Iterable, Dict, Union
from typing import Optional as Nullable, Iterable, Dict, Union, Tuple

from pyTooling.Decorators import export, readonly

Expand All @@ -60,7 +60,7 @@ class CoverageState(Flag):
@export
class Coverage:
_name: str
_parent: "Coverage"
_parent: Nullable["Coverage"]

_total: int
_excluded: int
Expand All @@ -71,7 +71,7 @@ class Coverage:

_coverage: float

def __init__(self, name: str, parent: Nullable["Coverage"] = None):
def __init__(self, name: str, parent: Nullable["Coverage"] = None) -> None:
self._name = name
self._parent = parent

Expand All @@ -89,7 +89,7 @@ def Name(self) -> str:
return self._name

@readonly
def Parent(self) -> "Coverage":
def Parent(self) -> Nullable["Coverage"]:
return self._parent

@readonly
Expand Down Expand Up @@ -120,14 +120,14 @@ def Uncovered(self) -> int:
def Coverage(self) -> float:
return self._coverage

def CalculateCoverage(self):
def CalculateCoverage(self) -> None:
self._uncovered = self._expected - self._covered
if self._expected != 0:
self._coverage = self._covered / self._expected
else:
self._coverage = 1.0

def _CountCoverage(self, iterator: Iterable[CoverageState]):
def _CountCoverage(self, iterator: Iterable[CoverageState]) -> Tuple[int, int, int, int, int]:
total = 0
excluded = 0
ignored = 0
Expand All @@ -153,6 +153,7 @@ def _CountCoverage(self, iterator: Iterable[CoverageState]):

@export
class AggregatedCoverage(Coverage):
_file: Path
_aggregatedTotal: int
_aggregatedExcluded: int
_aggregatedIgnored: int
Expand All @@ -162,6 +163,10 @@ class AggregatedCoverage(Coverage):

_aggregatedCoverage: float

@readonly
def File(self) -> Path:
return self._file

@readonly
def AggregatedTotal(self) -> int:
return self._aggregatedTotal
Expand Down Expand Up @@ -205,7 +210,7 @@ class ClassCoverage(Coverage):
_methods: Dict[str, CoverageState]
_classes: Dict[str, "ClassCoverage"]

def __init__(self, name: str, parent: Union["PackageCoverage", "ClassCoverage", None] = None):
def __init__(self, name: str, parent: Union["PackageCoverage", "ClassCoverage", None] = None) -> None:
super().__init__(name, parent)

if parent is not None:
Expand All @@ -215,7 +220,7 @@ def __init__(self, name: str, parent: Union["PackageCoverage", "ClassCoverage",
self._methods = {}
self._classes = {}

def CalculateCoverage(self):
def CalculateCoverage(self) -> None:
for cls in self._classes.values():
cls.CalculateCoverage()

Expand All @@ -230,13 +235,11 @@ def CalculateCoverage(self):

@export
class ModuleCoverage(AggregatedCoverage):
_file: Path
_name: str
_variables: Dict[str, CoverageState]
_functions: Dict[str, CoverageState]
_classes: Dict[str, ClassCoverage]

def __init__(self, file: Path, name: str, parent: Nullable["PackageCoverage"] = None):
def __init__(self, file: Path, name: str, parent: Nullable["PackageCoverage"] = None) -> None:
super().__init__(name, parent)

if parent is not None:
Expand All @@ -248,11 +251,7 @@ def __init__(self, file: Path, name: str, parent: Nullable["PackageCoverage"] =
self._functions = {}
self._classes = {}

@readonly
def File(self) -> Path:
return self._file

def CalculateCoverage(self):
def CalculateCoverage(self) -> None:
for cls in self._classes.values():
cls.CalculateCoverage()

Expand Down Expand Up @@ -285,15 +284,14 @@ def Aggregate(self) -> None:

@export
class PackageCoverage(AggregatedCoverage):
_file: Path
_fileCount: int
_variables: Dict[str, CoverageState]
_functions: Dict[str, CoverageState]
_classes: Dict[str, ClassCoverage]
_modules: Dict[str, ModuleCoverage]
_packages: Dict[str, "PackageCoverage"]

def __init__(self, file: Path, name: str, parent: Nullable["PackageCoverage"] = None):
def __init__(self, file: Path, name: str, parent: Nullable["PackageCoverage"] = None) -> None:
super().__init__(name, parent)

if parent is not None:
Expand All @@ -307,10 +305,6 @@ def __init__(self, file: Path, name: str, parent: Nullable["PackageCoverage"] =
self._modules = {}
self._packages = {}

@readonly
def File(self) -> Path:
return self._file

@readonly
def FileCount(self) -> int:
return self._fileCount
Expand All @@ -321,7 +315,7 @@ def __getitem__(self, key: str) -> Union["PackageCoverage", ModuleCoverage]:
except KeyError:
return self._packages[key]

def CalculateCoverage(self):
def CalculateCoverage(self) -> None:
for cls in self._classes.values():
cls.CalculateCoverage()

Expand Down
29 changes: 18 additions & 11 deletions sphinx_reports/DocCoverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,24 @@
**Report documentation coverage as Sphinx documentation page(s).**
"""
from pathlib import Path
from typing import Dict, Tuple, Any, List, Iterable, Mapping, Generator
from typing import Dict, Tuple, Any, List, Iterable, Mapping, Generator, TypedDict

from docutils import nodes
from pyTooling.Decorators import export

from sphinx_reports.Common import ReportExtensionError
from sphinx_reports.Sphinx import strip, LegendPosition, BaseDirective
from sphinx_reports.DataModel.DocumentationCoverage import PackageCoverage
from sphinx_reports.Common import ReportExtensionError, LegendPosition
from sphinx_reports.Sphinx import strip, BaseDirective
from sphinx_reports.DataModel.DocumentationCoverage import PackageCoverage, AggregatedCoverage
from sphinx_reports.Adapter.DocStrCoverage import Analyzer


class package_DictType(TypedDict):
name: str
directory: str
fail_below: int
levels: Dict[int, Dict[str, str]]


@export
class DocCoverage(BaseDirective):
"""
Expand Down Expand Up @@ -71,17 +78,17 @@ class DocCoverage(BaseDirective):
_levels: Dict[int, Dict[str, str]]
_coverage: PackageCoverage

def _CheckOptions(self):
def _CheckOptions(self) -> None:
# Parse all directive options or use default values
self._packageID = self._ParseStringOption("packageid")
self._legend = self._ParseLegendOption("legend", LegendPosition.Bottom)

def _CheckConfiguration(self):
def _CheckConfiguration(self) -> None:
from sphinx_reports import ReportDomain

# Check configuration fields and load necessary values
try:
allPackages = self.config[f"{ReportDomain.name}_{self.configPrefix}_packages"]
allPackages: Dict[str, package_DictType] = self.config[f"{ReportDomain.name}_{self.configPrefix}_packages"]
except (KeyError, AttributeError) as ex:
raise ReportExtensionError(f"Configuration option '{ReportDomain.name}_{self.configPrefix}_packages' is not configured.") from ex

Expand Down Expand Up @@ -120,7 +127,7 @@ def _CheckConfiguration(self):
100: {"class": "doccov-below100", "background": "rgba( 0, 200, 82, .2)", "desc": "excellent documented"},
}

def _ConvertToColor(self, currentLevel, configKey):
def _ConvertToColor(self, currentLevel: float, configKey: str) -> str:
for levelLimit, levelConfig in self._levels.items():
if (currentLevel * 100) < levelLimit:
return levelConfig[configKey]
Expand All @@ -143,11 +150,11 @@ def _GenerateCoverageTable(self) -> nodes.table:
tableBody = nodes.tbody()
tableGroup += tableBody

def sortedValues(d: Mapping) -> Generator[Any, None, None]:
def sortedValues(d: Mapping[str, AggregatedCoverage]) -> Generator[AggregatedCoverage, None, None]:
for key in sorted(d.keys()):
yield d[key]

def renderlevel(tableBody: nodes.tbody, packageCoverage: PackageCoverage, level: int = 0):
def renderlevel(tableBody: nodes.tbody, packageCoverage: PackageCoverage, level: int = 0) -> None:
tableBody += nodes.row(
"",
nodes.entry("", nodes.paragraph(text=f"{' '*level}{packageCoverage.Name} ({packageCoverage.File})")),
Expand Down Expand Up @@ -222,7 +229,7 @@ def _CreateLegend(self, identifier: str, classes: Iterable[str]) -> List[nodes.E

@export
class DocStrCoverage(DocCoverage):
def run(self):
def run(self) -> List[nodes.Node]:
self._CheckOptions()
self._CheckConfiguration()

Expand Down
4 changes: 2 additions & 2 deletions sphinx_reports/Sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@


@export
def strip(option: str):
def strip(option: str) -> str:
return option.strip().lower()


Expand Down Expand Up @@ -96,7 +96,7 @@ def _ParseBooleanOption(self, optionName: str, default: Nullable[bool] = None) -

def _ParseStringOption(self, optionName: str, default: Nullable[str] = None, regexp: str = "\\w+") -> str:
try:
option = self.options[optionName]
option: str = self.options[optionName]
except KeyError as ex:
if default is not None:
return default
Expand Down
Loading

0 comments on commit 235317d

Please sign in to comment.