From d15119d64f84123abd4cb7667dafdd125574d19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3n=C3=A1n=20Carrigan?= Date: Mon, 12 Oct 2020 18:17:22 +0100 Subject: [PATCH 1/5] remove instanceof type check for signature hints --- hypothesis-python/RELEASE.rst | 5 +++++ hypothesis-python/src/hypothesis/internal/compat.py | 2 +- hypothesis-python/tests/nocover/test_build_signature.py | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 hypothesis-python/RELEASE.rst diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst new file mode 100644 index 0000000000..f1ebb48f08 --- /dev/null +++ b/hypothesis-python/RELEASE.rst @@ -0,0 +1,5 @@ +RELEASE_TYPE: patch + +:func:`~hypothesis.interal.compat.get_type_hints` now accepts any hints present +in `thing.__signature__`, not just instances of `type`. This allows for the use +of types from the `typing` module. diff --git a/hypothesis-python/src/hypothesis/internal/compat.py b/hypothesis-python/src/hypothesis/internal/compat.py index 4d968ff51f..7a9edcfd06 100644 --- a/hypothesis-python/src/hypothesis/internal/compat.py +++ b/hypothesis-python/src/hypothesis/internal/compat.py @@ -135,7 +135,7 @@ def get_type_hints(thing): { k: v for k, v in spec.annotations.items() - if k in (spec.args + spec.kwonlyargs) and isinstance(v, type) + if k in (spec.args + spec.kwonlyargs) } ) except (AttributeError, TypeError, NameError): diff --git a/hypothesis-python/tests/nocover/test_build_signature.py b/hypothesis-python/tests/nocover/test_build_signature.py index dc57f984f5..8662cd951d 100644 --- a/hypothesis-python/tests/nocover/test_build_signature.py +++ b/hypothesis-python/tests/nocover/test_build_signature.py @@ -14,7 +14,7 @@ # END HEADER from inspect import signature -from typing import get_type_hints +from typing import get_type_hints, Literal from hypothesis import given, strategies as st @@ -53,7 +53,7 @@ def use_annotations( pass -def use_signature(self, testA: int, testB: str = None, *, testX: float, testY: str): +def use_signature(self, testA: int, testB: str = None, *, testX: float, testY: Literal["value"]): pass @@ -66,7 +66,7 @@ def __init__(self, **kwargs): assert set(kwargs) == {"testA", "testX", "testY"} assert isinstance(kwargs["testA"], int) assert isinstance(kwargs["testX"], float) - assert isinstance(kwargs["testY"], str) + assert kwargs["testY"] == "value" @given(st.builds(ModelWithAlias)) From e62e875ac57c1aeb3325039e1b90dbb507851e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3n=C3=A1n=20Carrigan?= Date: Tue, 13 Oct 2020 09:17:16 +0100 Subject: [PATCH 2/5] Typo in RELEASE.rst --- hypothesis-python/RELEASE.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst index f1ebb48f08..94d3f78647 100644 --- a/hypothesis-python/RELEASE.rst +++ b/hypothesis-python/RELEASE.rst @@ -1,5 +1,5 @@ RELEASE_TYPE: patch -:func:`~hypothesis.interal.compat.get_type_hints` now accepts any hints present +:func:`~hypothesis.internal.compat.get_type_hints` now accepts any hints present in `thing.__signature__`, not just instances of `type`. This allows for the use of types from the `typing` module. From 04c1b451cdc30870333ffb1a35c7d46563becde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3n=C3=A1n=20Carrigan?= Date: Tue, 13 Oct 2020 12:17:42 +0100 Subject: [PATCH 3/5] Add check for annotation to be a type --- .../src/hypothesis/internal/compat.py | 3 ++- .../tests/nocover/test_build_signature.py | 24 ++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/hypothesis-python/src/hypothesis/internal/compat.py b/hypothesis-python/src/hypothesis/internal/compat.py index 7a9edcfd06..648c5a88e2 100644 --- a/hypothesis-python/src/hypothesis/internal/compat.py +++ b/hypothesis-python/src/hypothesis/internal/compat.py @@ -130,12 +130,13 @@ def get_type_hints(thing): # comprehensive type information from get_type_hints # See https://github.com/HypothesisWorks/hypothesis/pull/2580 # for more details. + from hypothesis.strategies._internal.types import is_a_type spec = inspect.getfullargspec(thing) hints.update( { k: v for k, v in spec.annotations.items() - if k in (spec.args + spec.kwonlyargs) + if k in (spec.args + spec.kwonlyargs) and is_a_type(v) } ) except (AttributeError, TypeError, NameError): diff --git a/hypothesis-python/tests/nocover/test_build_signature.py b/hypothesis-python/tests/nocover/test_build_signature.py index 8662cd951d..146a6ccd6a 100644 --- a/hypothesis-python/tests/nocover/test_build_signature.py +++ b/hypothesis-python/tests/nocover/test_build_signature.py @@ -14,7 +14,7 @@ # END HEADER from inspect import signature -from typing import get_type_hints, Literal +from typing import get_type_hints, List from hypothesis import given, strategies as st @@ -53,7 +53,7 @@ def use_annotations( pass -def use_signature(self, testA: int, testB: str = None, *, testX: float, testY: Literal["value"]): +def use_signature(self, testA: int, testB: str = None, *, testX: float, testY: List[str]): pass @@ -66,9 +66,27 @@ def __init__(self, **kwargs): assert set(kwargs) == {"testA", "testX", "testY"} assert isinstance(kwargs["testA"], int) assert isinstance(kwargs["testX"], float) - assert kwargs["testY"] == "value" + assert isinstance(kwargs["testY"], list) + assert all(isinstance(elem, str) for elem in kwargs["testY"]) @given(st.builds(ModelWithAlias)) def test_build_using_different_signature_and_annotations(val): assert isinstance(val, ModelWithAlias) + + +def use_bad_signature(self, testA: 1, testB: "not_a_type", *, testX: float): + pass + + +class ModelWithBadAliasSignature: + __annotations__ = get_type_hints(use_annotations) + __signature__ = signature(use_bad_signature) + + def __init__(self, **kwargs): + assert set(kwargs) == {"testX"} + assert isinstance(kwargs["testX"], float) + +@given(st.builds(ModelWithBadAliasSignature)) +def test_build_with_non_types_in_signature(val): + assert isinstance(val, ModelWithBadAliasSignature) From 327a7dcc38ef5b1a3f0fdff28e468835c3473ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3n=C3=A1n=20Carrigan?= Date: Tue, 13 Oct 2020 12:19:34 +0100 Subject: [PATCH 4/5] Update hypothesis-python/RELEASE.rst Co-authored-by: Zac Hatfield-Dodds --- hypothesis-python/RELEASE.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst index 94d3f78647..5908aedd89 100644 --- a/hypothesis-python/RELEASE.rst +++ b/hypothesis-python/RELEASE.rst @@ -1,5 +1,8 @@ RELEASE_TYPE: patch -:func:`~hypothesis.internal.compat.get_type_hints` now accepts any hints present -in `thing.__signature__`, not just instances of `type`. This allows for the use -of types from the `typing` module. +This patch improves :func:`~hypothesis.strategies.builds` and +:func:`~hypothesis.strategies.from_type` support for explicitly defined ``__signature__`` +attributes, from :ref:`version 5.8.3 `, to support generic types from the +:mod:`python:typing` module. + +Thanks to Rónán Carrigan for identifying and fixing this problem! From 2b4291e2d7d1e48e41b2a22456789648519e7469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3n=C3=A1n=20Carrigan?= Date: Wed, 14 Oct 2020 07:51:18 +0100 Subject: [PATCH 5/5] Lint fixes --- hypothesis-python/src/hypothesis/internal/compat.py | 1 + hypothesis-python/tests/nocover/test_build_signature.py | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hypothesis-python/src/hypothesis/internal/compat.py b/hypothesis-python/src/hypothesis/internal/compat.py index 648c5a88e2..c1b5e6be03 100644 --- a/hypothesis-python/src/hypothesis/internal/compat.py +++ b/hypothesis-python/src/hypothesis/internal/compat.py @@ -131,6 +131,7 @@ def get_type_hints(thing): # See https://github.com/HypothesisWorks/hypothesis/pull/2580 # for more details. from hypothesis.strategies._internal.types import is_a_type + spec = inspect.getfullargspec(thing) hints.update( { diff --git a/hypothesis-python/tests/nocover/test_build_signature.py b/hypothesis-python/tests/nocover/test_build_signature.py index 146a6ccd6a..d55bd4c24d 100644 --- a/hypothesis-python/tests/nocover/test_build_signature.py +++ b/hypothesis-python/tests/nocover/test_build_signature.py @@ -14,7 +14,7 @@ # END HEADER from inspect import signature -from typing import get_type_hints, List +from typing import List, get_type_hints from hypothesis import given, strategies as st @@ -53,7 +53,9 @@ def use_annotations( pass -def use_signature(self, testA: int, testB: str = None, *, testX: float, testY: List[str]): +def use_signature( + self, testA: int, testB: str = None, *, testX: float, testY: List[str] +): pass @@ -75,7 +77,7 @@ def test_build_using_different_signature_and_annotations(val): assert isinstance(val, ModelWithAlias) -def use_bad_signature(self, testA: 1, testB: "not_a_type", *, testX: float): +def use_bad_signature(self, testA: 1, *, testX: float): pass @@ -87,6 +89,7 @@ def __init__(self, **kwargs): assert set(kwargs) == {"testX"} assert isinstance(kwargs["testX"], float) + @given(st.builds(ModelWithBadAliasSignature)) def test_build_with_non_types_in_signature(val): assert isinstance(val, ModelWithBadAliasSignature)