Repository: HypothesisWorks/hypothesis

Issue:
Improve argument validation for `st.lists()`
In [`validation.py`](https://github.com/HypothesisWorks/hypothesis/blob/master/hypothesis-python/src/hypothesis/internal/validation.py), we have a `check_valid_size` function, which should pass its `name` argument through to `check_type` to be included in the error message.

In the `check_type` function, we should unpack length-one tuples of types before checking if it's a tuple for message purposes; otherwise this leads to messages like "one of int" (because `integer_types` == `(int,)` on Python 3 but `(int, long)` on Py2).

Fix those, add yourself to the list of authors in `CONTRIBUTING.rst`, and create `hypothesis-python/RELEASE.rst` with the following contents:

```
RELEASE_TYPE: patch

This patch improves the error messages from invalid size arguments to
collection strategies such as :func:`~hypothesis.strategies.lists`.

Thanks to Your Name Here for the fix!
```


Discussion:
Hey Zac, taking a look at this now.

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/extra/numpy.py b/hypothesis-python/src/hypothesis/extra/numpy.py
--- a/hypothesis-python/src/hypothesis/extra/numpy.py
+++ b/hypothesis-python/src/hypothesis/extra/numpy.py
@@ -26,7 +26,7 @@
 from hypothesis import Verbosity
 from hypothesis._settings import note_deprecation
 from hypothesis.errors import InvalidArgument
-from hypothesis.internal.compat import hrange, integer_types
+from hypothesis.internal.compat import PY2, hrange, integer_types
 from hypothesis.internal.coverage import check_function
 from hypothesis.internal.reflection import proxies
 from hypothesis.internal.validation import check_type, check_valid_interval
@@ -615,11 +615,15 @@ def array_dtypes(
     """Return a strategy for generating array (compound) dtypes, with members
     drawn from the given subtype strategy."""
     order_check("size", 0, min_size, max_size)
-    native_strings = st.from_type(str).filter(bool)  # See issue #1798 re: filter!
-    elements = st.tuples(native_strings, subtype_strategy)
+    # Field names must be native strings and the empty string is weird; see #1963.
+    if PY2:
+        field_names = st.binary(min_size=1)
+    else:
+        field_names = st.text(min_size=1)
+    elements = st.tuples(field_names, subtype_strategy)
     if allow_subarrays:
         elements |= st.tuples(
-            native_strings, subtype_strategy, array_shapes(max_dims=2, max_side=2)
+            field_names, subtype_strategy, array_shapes(max_dims=2, max_side=2)
         )
     return st.lists(
         elements=elements,
diff --git a/hypothesis-python/src/hypothesis/internal/coverage.py b/hypothesis-python/src/hypothesis/internal/coverage.py
--- a/hypothesis-python/src/hypothesis/internal/coverage.py
+++ b/hypothesis-python/src/hypothesis/internal/coverage.py
@@ -50,7 +50,8 @@ def pretty_file_name(f):
         pass
 
     parts = f.split(os.path.sep)
-    parts = parts[-parts[::-1].index("hypothesis") :]
+    if "hypothesis" in parts:
+        parts = parts[-parts[::-1].index("hypothesis") :]
     result = os.path.sep.join(parts)
     pretty_file_name_cache[f] = result
     return result
diff --git a/hypothesis-python/src/hypothesis/internal/validation.py b/hypothesis-python/src/hypothesis/internal/validation.py
--- a/hypothesis-python/src/hypothesis/internal/validation.py
+++ b/hypothesis-python/src/hypothesis/internal/validation.py
@@ -31,6 +31,8 @@ def check_type(typ, arg, name=""):
     if name:
         name += "="
     if not isinstance(arg, typ):
+        if isinstance(typ, tuple) and len(typ) == 1:
+            typ = typ[0]
         if isinstance(typ, tuple):
             typ_string = "one of %s" % (", ".join(t.__name__ for t in typ))
         else:
@@ -120,7 +122,7 @@ def check_valid_size(value, name):
             since="2018-10-11",
         )
     else:
-        check_type(integer_types, value)
+        check_type(integer_types, value, name)
     if value < 0:
         raise InvalidArgument(u"Invalid size %s=%r < 0" % (name, value))
 


Test patch:
diff --git a/hypothesis-python/tests/cover/test_validation.py b/hypothesis-python/tests/cover/test_validation.py
--- a/hypothesis-python/tests/cover/test_validation.py
+++ b/hypothesis-python/tests/cover/test_validation.py
@@ -23,6 +23,7 @@
 
 from hypothesis import find, given
 from hypothesis.errors import InvalidArgument
+from hypothesis.internal.validation import check_type
 from hypothesis.strategies import (
     binary,
     booleans,
@@ -247,3 +248,15 @@ def test(x):
 
     with pytest.raises(InvalidArgument):
         test()
+
+
+def test_check_type_with_tuple_of_length_two():
+    # This test covers logic for length-two tuples that is essential on PY2,
+    # e.g. string_types (str, unicode) which are all length-one on Python 3.
+    def type_checker(x):
+        check_type((int, str), x, "x")
+
+    type_checker(1)
+    type_checker("1")
+    with pytest.raises(InvalidArgument, match="Expected one of int, str but got "):
+        type_checker(1.0)