From 4821ebc341270af08e05f866d43f9458ba64ba8a Mon Sep 17 00:00:00 2001 From: gram Date: Fri, 24 Mar 2023 09:08:07 +0100 Subject: [PATCH 1/2] upgrade typeguard for the return type check --- deal/_testing.py | 13 +++++++------ pyproject.toml | 4 ++-- tests/test_testing.py | 22 +++++++++++++++------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/deal/_testing.py b/deal/_testing.py index c40f9ce8..e526455b 100644 --- a/deal/_testing.py +++ b/deal/_testing.py @@ -62,13 +62,14 @@ def _check_result(self, result: Any) -> None: import typeguard except ImportError: return - memo = typeguard._CallMemo( - func=self.func, - args=self.args, - kwargs=self.kwargs, + memo = typeguard.CallMemo( + func=self.func, # type: ignore[arg-type] + frame_locals=self.kwargs, ) - typeguard.check_argument_types(memo=memo) - typeguard.check_return_type(result, memo=memo) + from typeguard._functions import check_return_type + + # check_argument_types(memo=memo) + check_return_type(result, memo=memo) class cases: # noqa: N diff --git a/pyproject.toml b/pyproject.toml index 45f61e06..200f4b1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ all = [ "deal-solver", "hypothesis", "pygments", - "typeguard", + "typeguard>=3.0.0", "vaa>=0.2.1", ] integration = [ # integration tests @@ -37,7 +37,7 @@ integration = [ # integration tests "deal-solver", "hypothesis", "pygments", - "typeguard<=2.13.3", + "typeguard", "vaa>=0.2.1", "sphinx>=4.5.0", "flake8", diff --git a/tests/test_testing.py b/tests/test_testing.py index 90eb2f34..cd540a36 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,3 +1,4 @@ +import re from typing import NoReturn, TypeVar import pytest @@ -13,6 +14,11 @@ import hypothesis.errors import hypothesis.strategies +try: + import typeguard +except ImportError: + typeguard = None + @deal.raises(ZeroDivisionError) def div1(a: int, b: int) -> float: @@ -37,11 +43,11 @@ def div2(a: int, b: int) -> float: @pytest.mark.skipif(hypothesis is None, reason='hypothesis is not installed') -def test_short_version_is_discoverable(): +def test_short_version_is_discoverable(monkeypatch): from _pytest.python import PyCollector collector = PyCollector.__new__(PyCollector) - collector._matches_prefix_or_glob_option = lambda *args: True + monkeypatch.setattr(collector, '_matches_prefix_or_glob_option', lambda *args: True) test = deal.cases(div1) assert collector.istestfunction(test, 'test_div') is True @@ -104,7 +110,8 @@ def div(a: int, b: int) -> int: def div(a: int, b: int) -> str: # type: ignore[no-redef] return 1 # type: ignore[return-value] - with pytest.raises(TypeError): + msg = re.escape('the return value (int) is not an instance of str') + with pytest.raises(typeguard.TypeCheckError, match=msg): case = next(iter(deal.cases(div, count=20))) case() @@ -140,8 +147,8 @@ def bad(a: int) -> str: # type is wrong and checked cases = deal.cases(bad, count=1) case = next(iter(cases)) - msg = 'type of the return value must be str; got int instead' - with pytest.raises(TypeError, match=msg): + msg = re.escape('the return value (int) is not an instance of str') + with pytest.raises(typeguard.TypeCheckError, match=msg): case() # type is wrong and ignored @@ -159,6 +166,7 @@ def good(a: int) -> int: @pytest.mark.skipif(hypothesis is None, reason='hypothesis is not installed') +@pytest.mark.skipif(typeguard is None, reason='typeguard is not installed') def test_return_type(): def identity(a) -> int: return a @@ -168,8 +176,8 @@ def identity(a) -> int: case() case = deal.TestCase(args=('hi', ), **kwargs) - msg = 'type of the return value must be int; got str instead' - with pytest.raises(TypeError, match=msg): + msg = re.escape('the return value (str) is not an instance of int') + with pytest.raises(typeguard.TypeCheckError, match=msg): case() From 981066797f7b6711fc262a2d29af14a234e9fef8 Mon Sep 17 00:00:00 2001 From: gram Date: Fri, 24 Mar 2023 09:13:48 +0100 Subject: [PATCH 2/2] typeguard: check kwargs type --- deal/_testing.py | 5 +++-- tests/test_testing.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/deal/_testing.py b/deal/_testing.py index e526455b..51135c32 100644 --- a/deal/_testing.py +++ b/deal/_testing.py @@ -66,9 +66,10 @@ def _check_result(self, result: Any) -> None: func=self.func, # type: ignore[arg-type] frame_locals=self.kwargs, ) - from typeguard._functions import check_return_type + from typeguard._functions import check_argument_types, check_return_type - # check_argument_types(memo=memo) + if not self.args and self.kwargs: + check_argument_types(memo=memo) check_return_type(result, memo=memo) diff --git a/tests/test_testing.py b/tests/test_testing.py index cd540a36..aa67a6d7 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -181,6 +181,22 @@ def identity(a) -> int: case() +@pytest.mark.skipif(hypothesis is None, reason='hypothesis is not installed') +@pytest.mark.skipif(typeguard is None, reason='typeguard is not installed') +def test_typecheck_explicit_kwargs(): + def identity(a: int) -> str: + return 'ok' + + kwargs: dict = dict(args=(), func=identity, exceptions=(), check_types=True) + case = deal.TestCase(kwargs={'a': 13}, **kwargs) + case() + + case = deal.TestCase(kwargs={'a': 'hi'}, **kwargs) + msg = re.escape('argument "a" (str) is not an instance of int') + with pytest.raises(typeguard.TypeCheckError, match=msg): + case() + + @pytest.mark.skipif(hypothesis is None, reason='hypothesis is not installed') def test_type_var(): T = TypeVar('T') # noqa: N806