Repository: HypothesisWorks/hypothesis

Issue:
Iterable strategy gives elements of wrong types
It appears to give me tuples of ints when asked for iterables of instances of my own class. Generating individual instances of my own class the same way (via annotation) works perfectly.

Version: 5.37.4. Also tried current master - same issue.

Here's an example:

```python
from typing import Iterable

from hypothesis import given, infer


class My:
    def __init__(self, a: str):
        self.a = a


@given(i=infer)
def test_iterable(i: Iterable[My]):
    assert all(isinstance(e, My) for e in i)
```

Fails with:

```
    @given(i=infer)
>   def test_iterable(i: Iterable[My]):

repro.py:12:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

i = (0,)

    @given(i=infer)
    def test_iterable(i: Iterable[My]):
>       assert all(isinstance(e, My) for e in i)
E       assert False
E        +  where False = all(<generator object test_iterable.<locals>.<genexpr> at 0x7fcb12b74510>)

repro.py:13: AssertionError
--------------------------- Hypothesis ---------------------------
Falsifying example: test_iterable(
    i=(0,),
)
```


Discussion:
Thanks for reporting this!  Definitely a bug, fortunately with a very simply fix which should be out soon :slightly_smiling_face: 
Thank you so much! I've started working on it myself, didn't notice you're on it and posted my own PR. It's inferior to what you did so I closed it.

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py
--- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py
@@ -365,10 +365,10 @@ def _networks(bits):
 
 _global_type_lookup.update(
     {
-        typing.ByteString: st.binary() | st.binary().map(bytearray),
-        # Reversible is somehow a subclass of Hashable, so we tuplize it.
-        # See also the discussion at https://bugs.python.org/issue39046
-        typing.Reversible: st.lists(st.integers()).map(tuple),
+        # Note: while ByteString notionally also represents the bytearray and
+        # memoryview types, it is a subclass of Hashable and those types are not.
+        # We therefore only generate the bytes type.
+        typing.ByteString: st.binary(),
         # TODO: SupportsAbs and SupportsRound should be covariant, ie have functions.
         typing.SupportsAbs: st.one_of(
             st.booleans(),
diff --git a/tooling/src/hypothesistooling/__main__.py b/tooling/src/hypothesistooling/__main__.py
--- a/tooling/src/hypothesistooling/__main__.py
+++ b/tooling/src/hypothesistooling/__main__.py
@@ -376,7 +376,7 @@ def run_tox(task, version):
 
 
 # Via https://github.com/pyenv/pyenv/tree/master/plugins/python-build/share/python-build
-PY36 = "3.6.9"
+PY36 = "3.6.12"
 PY37 = "3.7.9"
 PY38 = "3.8.6"
 PY39 = "3.9.0"


Test patch:
diff --git a/hypothesis-python/tests/cover/test_lookup.py b/hypothesis-python/tests/cover/test_lookup.py
--- a/hypothesis-python/tests/cover/test_lookup.py
+++ b/hypothesis-python/tests/cover/test_lookup.py
@@ -90,25 +90,30 @@ def test_typing_Type_Union(ex):
     assert ex in (str, list)
 
 
+class Elem:
+    pass
+
+
 @pytest.mark.parametrize(
     "typ,coll_type,instance_of",
     [
-        (typing.Set[int], set, int),
-        (typing.FrozenSet[int], frozenset, int),
-        (typing.Dict[int, int], dict, int),
-        (typing.DefaultDict[int, int], collections.defaultdict, int),
-        (typing.KeysView[int], type({}.keys()), int),
-        (typing.ValuesView[int], type({}.values()), int),
-        (typing.List[int], list, int),
-        (typing.Tuple[int], tuple, int),
-        (typing.Tuple[int, ...], tuple, int),
-        (typing.Iterator[int], typing.Iterator, int),
-        (typing.Sequence[int], typing.Sequence, int),
-        (typing.Iterable[int], typing.Iterable, int),
-        (typing.Mapping[int, None], typing.Mapping, int),
-        (typing.Container[int], typing.Container, int),
-        (typing.NamedTuple("A_NamedTuple", (("elem", int),)), tuple, int),
+        (typing.Set[Elem], set, Elem),
+        (typing.FrozenSet[Elem], frozenset, Elem),
+        (typing.Dict[Elem, Elem], dict, Elem),
+        (typing.DefaultDict[Elem, Elem], collections.defaultdict, Elem),
+        (typing.KeysView[Elem], type({}.keys()), Elem),
+        (typing.ValuesView[Elem], type({}.values()), Elem),
+        (typing.List[Elem], list, Elem),
+        (typing.Tuple[Elem], tuple, Elem),
+        (typing.Tuple[Elem, ...], tuple, Elem),
+        (typing.Iterator[Elem], typing.Iterator, Elem),
+        (typing.Sequence[Elem], typing.Sequence, Elem),
+        (typing.Iterable[Elem], typing.Iterable, Elem),
+        (typing.Mapping[Elem, None], typing.Mapping, Elem),
+        (typing.Container[Elem], typing.Container, Elem),
+        (typing.NamedTuple("A_NamedTuple", (("elem", Elem),)), tuple, Elem),
     ],
+    ids=repr,
 )
 @given(data=st.data())
 def test_specialised_collection_types(data, typ, coll_type, instance_of):
@@ -119,17 +124,17 @@ def test_specialised_collection_types(data, typ, coll_type, instance_of):
     assume(instances)  # non-empty collections without calling len(iterator)
 
 
-@given(from_type(typing.DefaultDict[int, int]).filter(len))
+@given(from_type(typing.DefaultDict[Elem, Elem]).filter(len))
 def test_defaultdict_values_type(ex):
-    assert all(isinstance(elem, int) for elem in ex.values())
+    assert all(isinstance(elem, Elem) for elem in ex.values())
 
 
-@given(from_type(typing.ItemsView[int, int]).filter(len))
+@given(from_type(typing.ItemsView[Elem, Elem]).filter(len))
 def test_ItemsView(ex):
     # See https://github.com/python/typing/issues/177
     assert isinstance(ex, type({}.items()))
     assert all(isinstance(elem, tuple) and len(elem) == 2 for elem in ex)
-    assert all(all(isinstance(e, int) for e in elem) for elem in ex)
+    assert all(all(isinstance(e, Elem) for e in elem) for elem in ex)
 
 
 def test_Optional_minimises_to_None():
diff --git a/hypothesis-python/tests/ghostwriter/recorded/timsort_idempotent.txt b/hypothesis-python/tests/ghostwriter/recorded/timsort_idempotent.txt
--- a/hypothesis-python/tests/ghostwriter/recorded/timsort_idempotent.txt
+++ b/hypothesis-python/tests/ghostwriter/recorded/timsort_idempotent.txt
@@ -5,7 +5,7 @@ import test_expected_output
 from hypothesis import given, strategies as st
 
 
-@given(seq=st.one_of(st.binary(), st.binary().map(bytearray), st.lists(st.integers())))
+@given(seq=st.one_of(st.binary(), st.lists(st.integers())))
 def test_idempotent_timsort(seq):
     result = test_expected_output.timsort(seq=seq)
     repeat = test_expected_output.timsort(seq=result)
diff --git a/hypothesis-python/tests/ghostwriter/test_ghostwriter.py b/hypothesis-python/tests/ghostwriter/test_ghostwriter.py
--- a/hypothesis-python/tests/ghostwriter/test_ghostwriter.py
+++ b/hypothesis-python/tests/ghostwriter/test_ghostwriter.py
@@ -21,7 +21,7 @@
 import unittest.mock
 from decimal import Decimal
 from types import ModuleType
-from typing import Any, List, Sequence, Set
+from typing import Any, List, Sequence, Set, Union
 
 import pytest
 
@@ -117,7 +117,7 @@ def non_resolvable_arg(x: NotResolvable):
 
 
 def test_flattens_one_of_repr():
-    strat = from_type(Sequence[int])
+    strat = from_type(Union[int, Sequence[int]])
     assert repr(strat).count("one_of(") > 1
     assert ghostwriter._valid_syntax_repr(strat).count("one_of(") == 1