diff --git a/.gitignore b/.gitignore index 410cb51..50d06e0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ __version.py .cache .eggs .tox +.idea/* build dist diff --git a/pyproject.toml b/pyproject.toml index 019ea01..0645dec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ test = [ "flake8>=4.0.1", "pre-commit>=2.17.0", "tox>=3.24.5", + "mypy>=1.16.0", ] [project.urls] @@ -77,3 +78,28 @@ exclude = [ [tool.hatch.build.hooks.vcs] version-file = "src/pytest_metadata/__version.py" + +[tool.mypy] +allow_redefinition = false +allow_untyped_globals = false +check_untyped_defs = true +disallow_incomplete_defs = true +disallow_subclassing_any = true +disallow_untyped_calls = true +disallow_untyped_decorators = true +disallow_untyped_defs = true +follow_imports = "silent" +ignore_missing_imports = false +implicit_reexport = false +no_implicit_optional = true +pretty = true +show_error_codes = true +strict_equality = true +warn_no_return = false +warn_return_any = true +warn_unreachable = true +warn_unused_ignores = true + +[[tool.mypy.overrides]] +module = ["_pytest._pluggy.*"] +ignore_missing_imports = true diff --git a/src/pytest_metadata/hooks.py b/src/pytest_metadata/hooks.py index 350d42e..7b11852 100644 --- a/src/pytest_metadata/hooks.py +++ b/src/pytest_metadata/hooks.py @@ -1,7 +1,10 @@ # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. +from typing import Dict +from pytest import Config -def pytest_metadata(metadata, config): + +def pytest_metadata(metadata: Dict, config: Config) -> None: """Called after collecting metadata""" diff --git a/src/pytest_metadata/plugin.py b/src/pytest_metadata/plugin.py index d4bf9ca..202b334 100644 --- a/src/pytest_metadata/plugin.py +++ b/src/pytest_metadata/plugin.py @@ -4,6 +4,9 @@ import json import os import platform +from typing import Dict, Callable + +from pytest import PytestPluginManager, Config, Parser try: import _pytest._pluggy as pluggy @@ -36,27 +39,31 @@ metadata_key = pytest.StashKey[dict]() -def pytest_addhooks(pluginmanager): +def pytest_addhooks(pluginmanager: PytestPluginManager) -> None: from pytest_metadata import hooks pluginmanager.add_hookspecs(hooks) @pytest.fixture(scope="session") -def metadata(pytestconfig): +def metadata(pytestconfig: Config) -> Dict: """Provide test session metadata""" return pytestconfig.stash[metadata_key] @pytest.fixture(scope="session") -def include_metadata_in_junit_xml(metadata, pytestconfig, record_testsuite_property): +def include_metadata_in_junit_xml( + metadata: Dict, + pytestconfig: Config, + record_testsuite_property: Callable[[str, object], None], +) -> None: """Provide test session metadata""" metadata_ = pytestconfig.stash[metadata_key] for name, value in metadata_.items(): record_testsuite_property(name, value) -def pytest_addoption(parser): +def pytest_addoption(parser: Parser) -> None: group = parser.getgroup("pytest-metadata") group.addoption( "--metadata", @@ -83,7 +90,7 @@ def pytest_addoption(parser): @pytest.hookimpl(tryfirst=True) -def pytest_configure(config): +def pytest_configure(config: Config) -> None: config.stash[metadata_key] = { "Python": platform.python_version(), "Platform": platform.platform(), @@ -118,13 +125,15 @@ def pytest_configure(config): config.hook.pytest_metadata(metadata=config.stash[metadata_key], config=config) -def pytest_report_header(config): +def pytest_report_header(config: Config) -> str | None: if config.getoption("verbose") > 0: return "metadata: {0}".format(config.stash[metadata_key]) @pytest.hookimpl(optionalhook=True) -def pytest_testnodedown(node): +def pytest_testnodedown( + node: "pytest_xdist.WorkerController", # noqa: F821 +) -> None: # type:ignore[name-defined] # note that any metadata from remote workers will be replaced with the # environment from the final worker to quit if hasattr(node, "workeroutput"): diff --git a/src/pytest_metadata/py.typed b/src/pytest_metadata/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_metadata.py b/tests/test_metadata.py index 4787755..a057aaf 100644 --- a/tests/test_metadata.py +++ b/tests/test_metadata.py @@ -2,12 +2,13 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. import pytest +from pytest import Pytester, MonkeyPatch from xml.etree import ElementTree as ET pytest_plugins = ("pytester",) -def test_metadata(pytester): +def test_metadata(pytester: Pytester) -> None: pytester.makepyfile( """ def test_pass(metadata): @@ -20,7 +21,7 @@ def test_pass(metadata): assert result.ret == 0 -def test_environment_variables(pytester, monkeypatch): +def test_environment_variables(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: monkeypatch.setenv("JENKINS_URL", "foo") monkeypatch.setenv("GIT_COMMIT", "bar") pytester.makepyfile( @@ -34,7 +35,7 @@ def test_pass(metadata): assert result.ret == 0 -def test_additional_metadata(pytester): +def test_additional_metadata(pytester: Pytester) -> None: pytester.makepyfile( """ def test_pass(metadata): @@ -49,7 +50,7 @@ def test_pass(metadata): @pytest.mark.parametrize("junit_format", ["xunit1", "xunit2"]) -def test_junit_integration(pytester, junit_format): +def test_junit_integration(pytester: Pytester, junit_format: str) -> None: pytester.makepyfile( """ import pytest @@ -78,7 +79,7 @@ def test_pass(): assert {"name": "Daffy", "value": "Duck"} in xml_metadata -def test_additional_metadata_from_json(pytester): +def test_additional_metadata_from_json(pytester: Pytester) -> None: pytester.makepyfile( """ def test_pass(metadata): @@ -89,7 +90,7 @@ def test_pass(metadata): assert result.ret == 0 -def test_additional_metadata_from_json_file(pytester): +def test_additional_metadata_from_json_file(pytester: Pytester) -> None: pytester.makepyfile( """ def test_pass(metadata): @@ -102,7 +103,9 @@ def test_pass(metadata): assert result.ret == 0 -def test_additional_metadata_using_key_values_json_str_and_file(pytester): +def test_additional_metadata_using_key_values_json_str_and_file( + pytester: Pytester, +) -> None: pytester.makepyfile( """ def test_pass(metadata): @@ -125,7 +128,7 @@ def test_pass(metadata): assert result.ret == 0 -def test_metadata_hook(pytester): +def test_metadata_hook(pytester: Pytester) -> None: pytester.makeconftest( """ import pytest @@ -144,7 +147,7 @@ def test_pass(metadata): assert result.ret == 0 -def test_report_header(pytester): +def test_report_header(pytester: Pytester) -> None: result = pytester.runpytest() assert not any(line.startswith("metadata:") for line in result.stdout.lines) result = pytester.runpytest("-v") diff --git a/tox.ini b/tox.ini index 032f74a..fb89d9c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{3.8, 3.9, 3.10, 3.11, 3.12, py3.10}, linting +envlist = py{3.8, 3.9, 3.10, 3.11, 3.12, py3.10}, linting, typing isolated_build = True [testenv] @@ -10,8 +10,14 @@ commands = pytest -s -ra --color=yes {posargs} [testenv:linting] skip_install = True basepython = python3 -deps = pre-commit -commands = pre-commit run --all-files --show-diff-on-failure + +[testenv:typing] +skip_install = True +basepython = python3 +deps = + pytest @ git+https://github.com/pytest-dev/pytest.git + mypy +commands = mypy . [testenv:devel] description = Tests with unreleased deps