Skip to content

Commit

Permalink
Test and fix attrs type inference when using the typing module (#2092)
Browse files Browse the repository at this point in the history
Test and fix attrs type inference when using the typing module
  • Loading branch information
Zac-HD committed Sep 17, 2019
2 parents 2c6b6f3 + cbfe533 commit bd19aba
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 11 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ their individual contributions.
* `JP Viljoen <https://github.com/froztbyte>`_ (froztbyte@froztbyte.net)
* `Jochen Müller <https://github.com/jomuel>`_
* `Joey Tuong <https://github.com/tetrapus>`_
* `Jonathan Gayvallet <https://github.com/Meallia>`_ (jonathan.gayvallet@orange.com)
* `Jonty Wareing <https://www.github.com/Jonty>`_ (jonty@jonty.co.uk)
* `Joshua Boone <https://www.github.com/patchedwork>`_ (joshuaboone4190@gmail.com)
* `jmhsi <https://www.github.com/jmhsi>`_
Expand Down
7 changes: 7 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
RELEASE_TYPE: patch

This patch fixes a bug in strategy inference for :pypi:`attrs` classes where
Hypothesis would fail to infer a strategy for attributes of a generic type
such as ``Union[int, str]`` or ``List[bool]`` (:issue:`2091`).

Thanks to Jonathan Gayvallet for the bug report and this patch!
9 changes: 1 addition & 8 deletions hypothesis-python/src/hypothesis/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,7 @@ def __init__(self, test_runner, search_strategy, test, settings, random, had_see

self.used_examples_from_database = False

def execute(
self,
data,
print_example=False,
is_final=False,
expected_failure=None,
collect=False,
):
def execute(self, data, print_example=False, is_final=False, expected_failure=None):
text_repr = [None]
if self.settings.deadline is None:
test = self.test
Expand Down
6 changes: 5 additions & 1 deletion hypothesis-python/src/hypothesis/internal/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,11 @@ def qualname(f):
typing_root_type = (typing._Final, typing._GenericAlias) # type: ignore
ForwardRef = typing.ForwardRef # type: ignore
except AttributeError:
typing_root_type = (typing.TypingMeta, typing.TypeVar) # type: ignore
typing_root_type = (
typing.TypingMeta,
typing.TypeVar,
typing._Union,
) # type: ignore
ForwardRef = typing._ForwardRef # type: ignore


Expand Down
4 changes: 2 additions & 2 deletions hypothesis-python/src/hypothesis/searchstrategy/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import hypothesis.strategies as st
from hypothesis.errors import ResolutionFailed
from hypothesis.internal.compat import get_type_hints, string_types
from hypothesis.searchstrategy.types import type_sorting_key
from hypothesis.searchstrategy.types import is_a_type, type_sorting_key
from hypothesis.utils.conventions import infer


Expand Down Expand Up @@ -124,7 +124,7 @@ def types_to_strategy(attrib, types):

# Otherwise, try the `type` attribute as a fallback, and finally try
# the type hints on a converter (desperate!) before giving up.
if isinstance(getattr(attrib, "type", None), type):
if is_a_type(getattr(attrib, "type", None)):
# The convoluted test is because variable annotations may be stored
# in string form; attrs doesn't evaluate them and we don't handle them.
# See PEP 526, PEP 563, and Hypothesis issue #1004 for details.
Expand Down
13 changes: 13 additions & 0 deletions hypothesis-python/tests/cover/test_attrs_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
from hypothesis import given, infer
from hypothesis.errors import ResolutionFailed

try:
import typing
except ImportError:
typing = None


@attr.s
class Inferrables(object):
Expand Down Expand Up @@ -54,6 +59,14 @@ class Inferrables(object):
validator_in_multiple_strings = attr.ib(
validator=[attr.validators.in_("abcd"), attr.validators.in_(["ab", "cd"])]
)

if typing is not None:
typing_list = attr.ib(type=typing.List[int])
typing_list_of_list = attr.ib(type=typing.List[typing.List[int]])
typing_dict = attr.ib(type=typing.Dict[str, int])
typing_union = attr.ib(type=typing.Optional[bool])
typing_union = attr.ib(type=typing.Union[str, int])

has_default = attr.ib(default=0)
has_default_factory = attr.ib(default=attr.Factory(list))
has_default_factory_takes_self = attr.ib( # uninferrable but has default
Expand Down

0 comments on commit bd19aba

Please sign in to comment.