Skip to content

Commit

Permalink
Run static type checking with Mypy
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarrmondragon committed Nov 22, 2023
1 parent bc375ad commit b802e5d
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 19 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:
"ubuntu-py310",
"ubuntu-py311",
"ubuntu-py312",

"ubuntu-mypy",
]

include:
Expand Down Expand Up @@ -99,6 +101,11 @@ jobs:
os: ubuntu-latest
tox_env: "py312"

- name: "ubuntu-mypy"
python: "3.12"
os: ubuntu-latest
tox_env: "mypy"

steps:
- uses: actions/checkout@v3

Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,8 @@ requires = [
]
build-backend = "setuptools.build_meta"

[tool.mypy]
warn_unused_configs = true
warn_unused_ignores = true

[tool.setuptools_scm]
13 changes: 10 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,24 @@ classifiers =
keywords = test, unittest, pytest

[options]
py_modules = pytest_subtests
install_requires =
pytest>=7.0
attrs>=19.2.0
python_requires = >=3.7
packages = find:
package_dir =
=src
= src
setup_requires =
setuptools
setuptools-scm>=6.0

[options.packages.find]
where = src

[options.entry_points]
pytest11 =
subtests = pytest_subtests
subtests = pytest_subtests.plugin

# Include src/py.typed in the distribution.
[options.package_data]
pytest_subtests = py.typed
Empty file added src/pytest_subtests/__init__.py
Empty file.
40 changes: 25 additions & 15 deletions src/pytest_subtests.py → src/pytest_subtests/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from __future__ import annotations

import time
from contextlib import contextmanager
from contextlib import nullcontext
from typing import Any
from typing import Mapping

import attr
import pytest
Expand All @@ -17,7 +21,7 @@
from _pytest.unittest import TestCaseFunction


def pytest_addoption(parser):
def pytest_addoption(parser: pytest.Parser) -> None:
group = parser.getgroup("subtests")
group.addoption(
"--no-subtests-shortletter",
Expand All @@ -35,15 +39,15 @@ class SubTestContext:


@attr.s(init=False)
class SubTestReport(TestReport):
class SubTestReport(TestReport): # type: ignore[misc]
context = attr.ib()

@property
def head_line(self):
def head_line(self) -> str:
_, _, domain = self.location
return f"{domain} {self.sub_test_description()}"

def sub_test_description(self):
def sub_test_description(self) -> str:
parts = []
if isinstance(self.context.msg, str):
parts.append(f"[{self.context.msg}]")
Expand All @@ -54,7 +58,7 @@ def sub_test_description(self):
parts.append(f"({params_desc})")
return " ".join(parts) or "(<subtest>)"

def _to_json(self):
def _to_json(self) -> dict:
data = super()._to_json()
del data["context"]
data["_report_type"] = "SubTestReport"
Expand Down Expand Up @@ -95,9 +99,9 @@ def _addSubTest(self, test_case, test, exc_info):
)


def pytest_configure(config):
TestCaseFunction.addSubTest = _addSubTest
TestCaseFunction.failfast = False
def pytest_configure(config: pytest.Config) -> None:
TestCaseFunction.addSubTest = _addSubTest # type: ignore[attr-defined]
TestCaseFunction.failfast = False # type: ignore[attr-defined]

# Hack (#86): the terminal does not know about the "subtests"
# status, so it will by default turn the output to yellow.
Expand All @@ -110,7 +114,7 @@ def pytest_configure(config):
# We need to check if we are not re-adding because we run our own tests
# with pytester in-process mode, so this will be called multiple times.
if new_types[0] not in _pytest.terminal.KNOWN_TYPES:
_pytest.terminal.KNOWN_TYPES = _pytest.terminal.KNOWN_TYPES + new_types
_pytest.terminal.KNOWN_TYPES = _pytest.terminal.KNOWN_TYPES + new_types # type: ignore[assignment]

_pytest.terminal._color_for_type.update(
{
Expand All @@ -121,7 +125,7 @@ def pytest_configure(config):
)


def pytest_unconfigure():
def pytest_unconfigure() -> None:
if hasattr(TestCaseFunction, "addSubTest"):
del TestCaseFunction.addSubTest
if hasattr(TestCaseFunction, "failfast"):
Expand Down Expand Up @@ -193,7 +197,7 @@ def _capturing_logs(self):
yield captured_logs

@contextmanager
def test(self, msg=None, **kwargs):
def test(self, msg: str | None = None, **kwargs):
start = time.time()
precise_start = time.perf_counter()
exc_info = None
Expand Down Expand Up @@ -257,7 +261,7 @@ class Captured:
out = attr.ib(default="", type=str)
err = attr.ib(default="", type=str)

def update_report(self, report):
def update_report(self, report: pytest.TestReport):
if self.out:
report.sections.append(("Captured stdout call", self.out))
if self.err:
Expand All @@ -282,15 +286,19 @@ def pytest_report_to_serializable(report):
return report._to_json()


def pytest_report_from_serializable(data):
def pytest_report_from_serializable(data: dict[str, Any]):
if data.get("_report_type") == "SubTestReport":
return SubTestReport._from_json(data)
return None


@pytest.hookimpl(tryfirst=True)
def pytest_report_teststatus(report, config):
def pytest_report_teststatus(
report: pytest.TestReport,
config: pytest.Config,
) -> tuple[str, str, str | Mapping[str, bool]] | None:
if report.when != "call" or not isinstance(report, SubTestReport):
return
return None

if hasattr(report, "wasxfail"):
return None
Expand All @@ -306,3 +314,5 @@ def pytest_report_teststatus(report, config):
elif outcome == "failed":
short = "" if config.option.no_subtests_shortletter else "u"
return outcome, short, f"{description} SUBFAIL"

return None
Empty file added src/pytest_subtests/py.typed
Empty file.
6 changes: 5 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[tox]
envlist = py37,py38,py39,py310,py311,py312
envlist = py37,py38,py39,py310,py311,py312,mypy

[testenv]
passenv =
Expand All @@ -12,3 +12,7 @@ deps =

commands =
pytest {posargs:tests}

[testenv:mypy]
deps = mypy
commands = mypy src

0 comments on commit b802e5d

Please sign in to comment.