Skip to content

Commit

Permalink
Merge pull request #178 from econchick/type-hints
Browse files Browse the repository at this point in the history
Add type hints (finally)
  • Loading branch information
econchick authored May 20, 2024
2 parents 73128e3 + 68193e3 commit 5a22acb
Show file tree
Hide file tree
Showing 27 changed files with 382 additions and 228 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
branches: [ master ]
# Allow rebuilds via API.
repository_dispatch:
types: rebuild
types: [rebuild]

env:
FORCE_COLOR: "1"
Expand Down Expand Up @@ -51,6 +51,14 @@ 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: 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 Expand Up @@ -127,6 +135,9 @@ jobs:
- name: "Create a badge"
run: interrogate --config pyproject.toml --generate-badge . src tests
if: runner.os != 'Windows'
env:
# TODO: set for only macos
DYLD_FALLBACK_LIBRARY_PATH: "/opt/homebrew/lib"

docs:
name: Check docs
Expand Down
24 changes: 22 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,31 @@ To generate a **PNG file** instead, install ``interrogate`` with the extras ``[p
**NOTICE:** Additional system libraries/tools may be required in order to generate a PNG file of the coverage badge:

* on Windows, install Visual C++ compiler for Cairo;
* on macOS, install ``cairo`` and ``libffi`` (with Homebrew for example);
* on macOS, install ``cairo`` and ``libffi`` (with Homebrew for example - `see note below <#macos-and-cairo>`_);
* on Linux, install the ``cairo``, ``python3-dev`` and ``libffi-dev`` packages (names may vary depending on distribution).

Refer to the ``cairosvg`` `documentation <https://cairosvg.org/documentation/>`_ for more information.

MacOS and Cairo
^^^^^^^^^^^^^^^

If you get an error when trying to generate a badge like so:

.. code-block:: console
OSError: no library called "cairo-2" was found
no library called "cairo" was found
no library called "libcairo-2" was found
Then first try:

.. code-block:: console
export DYLD_FALLBACK_LIBRARY_PATH=/opt/homebrew/lib
And rerun the command.

Usage
=====

Expand Down Expand Up @@ -373,7 +393,7 @@ Configure within your ``pyproject.toml`` (``interrogate`` will automatically det
ignore-regex = ["^get$", "^mock_.*", ".*BaseClass.*"]
ext = []
# possible values: sphinx (default), google
style = sphinx
style = "sphinx"
# possible values: 0 (minimal output), 1 (-v), 2 (-vv)
verbose = 0
quiet = false
Expand Down
3 changes: 3 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif

# Internal variables
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .

.PHONY: help clean html livehtml linkcheck

help:
Expand Down
8 changes: 8 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Changelog
=========

1.8.0 (UNRELEASED)
------------------

Added
^^^^^

* Finally added type hints

.. short-log
1.7.0 (2024-04-07)
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
60 changes: 41 additions & 19 deletions src/interrogate/badge_gen.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# 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

from importlib import resources
from typing import Union
from xml.dom import minidom


Expand All @@ -16,9 +18,13 @@
except ImportError: # pragma: no cover
cairosvg = None

from interrogate.coverage import InterrogateResults

DEFAULT_FILENAME = "interrogate_badge"
COLORS = {

NumberType = Union[int, float]

DEFAULT_FILENAME: str = "interrogate_badge"
COLORS: dict[str, str] = {
"brightgreen": "#4c1",
"green": "#97CA00",
"yellowgreen": "#a4a61d",
Expand All @@ -28,19 +34,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 +79,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 +126,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 +145,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 +166,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 +183,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 +198,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 +240,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 +259,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 +280,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

0 comments on commit 5a22acb

Please sign in to comment.