Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add type hints (finally) #178

Merged
merged 12 commits into from
May 20, 2024
4 changes: 4 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ jobs:
- name: Run tox targets for ${{ matrix.python-version }}
run: python -Im tox run -f py$(echo ${{ matrix.python-version }} | tr -d .)

- name: Run mypy
run: python -Im tox run -e mypy
if: matrix.python-version == '3.11'

- name: Check MANIFEST.in
run: python -Im tox run -e manifest
if: matrix.python-version == '3.11'
Expand Down
8 changes: 5 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,8 @@ fail_under = 95
line-length = 79

[tool.isort]
atomic=true
force_single_line=true
lines_after_imports=2
lines_between_types=1
use_parentheses=true
known_first_party="interrogate"
known_third_party=["attr", "click", "py", "pytest", "setuptools", "tabulate"]

Expand All @@ -53,3 +50,8 @@ quiet = false
whitelist-regex = []
ignore-regex = []
color = true

[tool.mypy]
strict = true
pretty = true
ignore_missing_imports = true
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
import os
import re

from setuptools import find_packages
from setuptools import setup
from setuptools import find_packages, setup


HERE = os.path.abspath(os.path.dirname(__file__))
Expand Down Expand Up @@ -80,11 +79,13 @@ def find_meta(meta):
"png": ["cairosvg"],
"docs": ["sphinx", "sphinx-autobuild"],
"tests": ["pytest", "pytest-cov", "pytest-mock", "coverage[toml]"],
"typing": ["mypy", "types-tabulate"],
}
EXTRAS_REQUIRE["dev"] = (
EXTRAS_REQUIRE["png"]
+ EXTRAS_REQUIRE["docs"]
+ EXTRAS_REQUIRE["tests"]
+ EXTRAS_REQUIRE["typing"]
+ ["wheel", "pre-commit"]
)
URL = find_meta("uri")
Expand Down
2 changes: 1 addition & 1 deletion src/interrogate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020-2021 Lynn Root
# Copyright 2020-2024 Lynn Root
"""Explain yourself! Interrogate a codebase for docstring coverage."""
__author__ = "Lynn Root"
__version__ = "1.7.0"
Expand Down
2 changes: 1 addition & 1 deletion src/interrogate/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020 Lynn Root
# Copyright 2020-2024 Lynn Root
"""interrogate entrypoint"""

from interrogate import cli
Expand Down
59 changes: 40 additions & 19 deletions src/interrogate/badge_gen.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright 2020 Lynn Root
# Copyright 2020-2024 Lynn Root
"""Module for generating an SVG badge.

Inspired by `coverage-badge <https://github.com/dbrgn/coverage-badge>`_.
"""
from __future__ import annotations

import os
import sys
Expand All @@ -16,9 +17,13 @@
except ImportError: # pragma: no cover
cairosvg = None

from interrogate.coverage import InterrogateResults

DEFAULT_FILENAME = "interrogate_badge"
COLORS = {

NumberType = int | float

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This causes CI to fail as int | float is actually executed at runtime (since it's not an annotation). In this case you still have to use the <3.10 syntax:

Suggested change
NumberType = int | float
NumberType = Union[int, float]

Or, better yet, don't introduce this type alias at all and simply use int | float everywhere (it's just one more character 😉)

DEFAULT_FILENAME: str = "interrogate_badge"
COLORS: dict[str, str] = {
"brightgreen": "#4c1",
"green": "#97CA00",
"yellowgreen": "#a4a61d",
Expand All @@ -28,19 +33,21 @@
"lightgrey": "#9f9f9f",
}

COLOR_RANGES = [
COLOR_RANGES: list[tuple[int, str]] = [
(95, "brightgreen"),
(90, "green"),
(75, "yellowgreen"),
(60, "yellow"),
(40, "orange"),
(0, "red"),
]
SUPPORTED_OUTPUT_FORMATS = ["svg", "png"]
SUPPORTED_OUTPUT_FORMATS: list[str] = ["svg", "png"]
# depending on the character length of the result (e.g. 100, 99.9, 9.9)
# a few values in the svg template need to adjust so it's readable.
# Tuple of values: (svg_width, rect_width, text_x, text_length)
SVG_WIDTH_VALUES = {
SVG_WIDTH_VALUES: dict[
str, dict[str, tuple[int, int, NumberType, NumberType]]
] = {
# integer
"100": {
"plastic": (135, 43, 1140, 330),
Expand Down Expand Up @@ -71,7 +78,9 @@
}


def save_badge(badge, output, output_format=None):
def save_badge(
badge: str, output: str, output_format: str | None = None
) -> str:
"""Save badge to the specified path.

.. versionadded:: 1.4.0 new ``output_format`` keyword argument
Expand Down Expand Up @@ -116,7 +125,9 @@ def save_badge(badge, output, output_format=None):
return output


def _get_badge_measurements(result, style):
def _get_badge_measurements(
result: float, style: str
) -> dict[str, NumberType]:
"""Lookup templated style values based on result number."""
if result == 100:
width_values = SVG_WIDTH_VALUES["100"]
Expand All @@ -133,15 +144,15 @@ def _get_badge_measurements(result, style):
}


def _format_result(result):
def _format_result(result: float) -> str:
"""Format result into string for templating."""
# do not include decimal if it's 100
if result == 100:
return "100"
return f"{result:.1f}"


def get_badge(result, color, style=None):
def get_badge(result: float, color: str, style: str | None = None) -> str:
"""Generate an SVG from template.

:param float result: coverage % result.
Expand All @@ -154,9 +165,9 @@ def get_badge(result, color, style=None):
style = "flat-square-modified"
template_file = f"{style}-style.svg"
badge_template_values = _get_badge_measurements(result, style)
result = _format_result(result)
badge_template_values["result"] = result
badge_template_values["color"] = color
formatted_result = _format_result(result)
badge_template_values["result"] = formatted_result # type: ignore
badge_template_values["color"] = color # type: ignore

if sys.version_info >= (3, 9):
tmpl = (
Expand All @@ -171,7 +182,7 @@ def get_badge(result, color, style=None):
return tmpl


def should_generate_badge(output, color, result):
def should_generate_badge(output: str, color: str, result: float) -> bool:
"""Detect if existing badge needs updating.

This is to help avoid unnecessary newline updates. See
Expand All @@ -186,8 +197,8 @@ def should_generate_badge(output, color, result):
logo doesn't exist.

:param str output: path to output badge file
:param float result: coverage % result.
:param str color: color of badge.
:param float result: coverage % result.
:return: Whether or not the badge SVG file should be generated.
:rtype: bool
"""
Expand Down Expand Up @@ -228,13 +239,13 @@ def should_generate_badge(output, color, result):
for t in texts
if t.hasAttribute("data-interrogate")
]
result = f"{result:.1f}%"
if result in current_results:
formatted_result = f"{result:.1f}%"
if formatted_result in current_results:
return False
return True


def get_color(result):
def get_color(result: float) -> str:
"""Get color for current doc coverage percent.

:param float result: coverage % result
Expand All @@ -247,7 +258,12 @@ def get_color(result):
return COLORS["lightgrey"]


def create(output, result, output_format=None, output_style=None):
def create(
output: str,
result: InterrogateResults,
output_format: str | None = None,
output_style: str | None = None,
) -> str:
"""Create a status badge.

The badge file will only be written if it doesn't exist, or if the
Expand All @@ -263,6 +279,11 @@ def create(output, result, output_format=None, output_style=None):
:param str output: path to output badge file.
:param coverage.InterrogateResults result: results of coverage
interrogation.
:param str output_format: output format of the badge. Options: "svg", "png".
Default: "svg"
:param str output_style: badge styling. Options: "plastic", "social",
"flat", "flat-square", "flat-square-modified", "for-the-badge".
Default: "flat-square-modified"
:return: path to output badge file.
:rtype: str
"""
Expand Down
Loading
Loading