Repository: HypothesisWorks/hypothesis
Pull request: Make `hypothesis.infer` an alias for `...` and enable `@given(...)` to infer-all
Closes #3188 

Problem statement:
Permit `...` in place of `hypothesis.infer`, and enable users to write `@given(...)` to mean "infer everything"
I propose that `Ellipses`, or `...`, be used in place of `hypothesis.infer` in the context of `@given`. E.g.

```python
from hypothesis import given, infer

@given(x=infer, y=infer)
def test_me(x: int, y: str):
    # code here
```

could be re-written as

```python
@given(x=..., y=...)
def test_me(x: int, y: str):
    # code here
```

It does not seem like there is any place where `Ellipses` could be confused with a strategy, thus there would not be any compatibility issues here.

@Zac-HD  also had the idea that we can also permit:


```python
@given(...)
def test_me(x: int, y: str):
    # code here
```

which can be taken to mean "infer all arguments".

There was also some discussion of deprecating `hypothesis.infer` altogether in favor of this syntax.

Hints from issue discussion:

Commit message: Remove references to infer in error messages and docstrings

Patch to review:
diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst
new file mode 100644
index 0000000000..0ebc45aa05
--- /dev/null
+++ b/hypothesis-python/RELEASE.rst
@@ -0,0 +1,6 @@
+RELEASE_TYPE: minor
+
+This release changes the implementation of :const:`~hypothesis.infer` to be an alias 
+for :obj:`python:Ellipsis`. E.g. ``@given(a=infer)`` is now equivalent to ``@given(a=...)``. Furthermore, ``@given(...)`` can now be specified so that 
+:func:`@given <hypothesis.given>` will infer the strategies for *all* arguments of the
+decorated function based on its annotations.
\ No newline at end of file
diff --git a/hypothesis-python/docs/details.rst b/hypothesis-python/docs/details.rst
index bc589fd0d6..fc7b3ba637 100644
--- a/hypothesis-python/docs/details.rst
+++ b/hypothesis-python/docs/details.rst
@@ -561,7 +561,7 @@ limits.
 If there are required arguments with type annotations and
 no strategy was passed to :func:`~hypothesis.strategies.builds`,
 :func:`~hypothesis.strategies.from_type` is used to fill them in.
-You can also pass the special value :const:`hypothesis.infer` as a keyword
+You can also pass the value ``...`` (``Ellipsis``) as a keyword
 argument, to force this inference for arguments with a default value.
 
 .. code-block:: pycon
@@ -576,21 +576,38 @@ argument, to force this inference for arguments with a default value.
 
 :func:`@given <hypothesis.given>` does not perform any implicit inference
 for required arguments, as this would break compatibility with pytest fixtures.
-:const:`~hypothesis.infer` can be used as a keyword argument to explicitly
-fill in an argument from its type annotation.
+:const:`~hypothesis.infer`, which is an alias of ``...`` (:obj:`python:Ellipsis`), can 
+be used as a keyword argument to explicitly fill in an argument from its type 
+annotation.
 
 .. code:: python
 
-    @given(a=infer)
+    @given(a=...)  # or @given(a=infer)
     def test(a: int):
         pass
 
 
     # is equivalent to
-    @given(a=integers())
+    @given(a=from_type(int))
     def test(a):
         pass
 
+
+``@given(...)`` can also be specified to fill all arguments from their type annotations.
+
+.. code:: python
+
+    @given(...)
+    def test(a: int, b: str):
+        pass
+
+
+    # is equivalent to
+    @given(a=..., b=...)
+    def test(a, b):
+        pass
+
+
 ~~~~~~~~~~~
 Limitations
 ~~~~~~~~~~~
diff --git a/hypothesis-python/src/hypothesis/core.py b/hypothesis-python/src/hypothesis/core.py
index 6c3f838d5b..28418d705b 100644
--- a/hypothesis-python/src/hypothesis/core.py
+++ b/hypothesis-python/src/hypothesis/core.py
@@ -22,6 +22,7 @@
 import zlib
 from collections import defaultdict
 from io import StringIO
+from itertools import chain
 from random import Random
 from typing import (
     Any,
@@ -105,10 +106,17 @@
     MappedSearchStrategy,
     SearchStrategy,
 )
-from hypothesis.utils.conventions import InferType, infer
+from hypothesis.utils.conventions import infer
 from hypothesis.vendor.pretty import RepresentationPrinter
 from hypothesis.version import __version__
 
+if sys.version_info >= (3, 10):  # pragma: no cover
+    from types import EllipsisType as InferType
+
+else:
+    InferType = type(Ellipsis)
+
+
 TestFunc = TypeVar("TestFunc", bound=Callable)
 
 
@@ -274,8 +282,9 @@ def wrapped_test(*arguments, **kwargs):
 
     if infer in given_arguments:
         return invalid(
-            "infer was passed as a positional argument to @given, "
-            "but may only be passed as a keyword argument"
+            "... was passed as a positional argument to @given, "
+            "but may only be passed as a keyword argument or as "
+            "the sole argument of @given"
         )
 
     if given_arguments and given_kwargs:
@@ -970,6 +979,14 @@ def run_test_as_given(test):
 
         original_argspec = getfullargspec(test)
 
+        if given_arguments == (Ellipsis,) and not given_kwargs:
+            # user indicated that they want to infer all arguments
+            given_kwargs.update(
+                (name, Ellipsis)
+                for name in chain(original_argspec.args, original_argspec.kwonlyargs)
+            )
+            given_arguments = ()
+
         check_invalid = is_invalid_test(
             test, original_argspec, given_arguments, given_kwargs
         )
@@ -1003,7 +1020,7 @@ def run_test_as_given(test):
                 def wrapped_test(*arguments, **kwargs):
                     __tracebackhide__ = True
                     raise InvalidArgument(
-                        f"passed {name}=infer for {test.__name__}, but {name} has "
+                        f"passed {name}=... for {test.__name__}, but {name} has "
                         "no type annotation"
                     )
 
diff --git a/hypothesis-python/src/hypothesis/errors.py b/hypothesis-python/src/hypothesis/errors.py
index 469eb8337c..a864c7374e 100644
--- a/hypothesis-python/src/hypothesis/errors.py
+++ b/hypothesis-python/src/hypothesis/errors.py
@@ -79,7 +79,7 @@ class ResolutionFailed(InvalidArgument):
 
     Type inference is best-effort, so this only happens when an
     annotation exists but could not be resolved for a required argument
-    to the target of ``builds()``, or where the user passed ``infer``.
+    to the target of ``builds()``, or where the user passed ``...``.
     """
 
 
diff --git a/hypothesis-python/src/hypothesis/extra/django/_fields.py b/hypothesis-python/src/hypothesis/extra/django/_fields.py
index 8605379146..9a2fd717f5 100644
--- a/hypothesis-python/src/hypothesis/extra/django/_fields.py
+++ b/hypothesis-python/src/hypothesis/extra/django/_fields.py
@@ -290,7 +290,8 @@ def from_field(field: F) -> st.SearchStrategy[Union[F, None]]:
 
     This function is used by :func:`~hypothesis.extra.django.from_form` and
     :func:`~hypothesis.extra.django.from_model` for any fields that require
-    a value, or for which you passed :obj:`hypothesis.infer`.
+    a value, or for which you passed ``...`` (:obj:`python:Ellipsis`) to infer
+    a strategy from an annotation.
 
     It's pretty similar to the core :func:`~hypothesis.strategies.from_type`
     function, with a subtle but important difference: ``from_field`` takes a
diff --git a/hypothesis-python/src/hypothesis/extra/django/_impl.py b/hypothesis-python/src/hypothesis/extra/django/_impl.py
index 853af27e1a..d508312d8a 100644
--- a/hypothesis-python/src/hypothesis/extra/django/_impl.py
+++ b/hypothesis-python/src/hypothesis/extra/django/_impl.py
@@ -24,7 +24,13 @@
 from hypothesis.extra.django._fields import from_field
 from hypothesis.internal.reflection import define_function_signature_from_signature
 from hypothesis.strategies._internal.utils import defines_strategy
-from hypothesis.utils.conventions import InferType, infer
+from hypothesis.utils.conventions import infer
+
+if sys.version_info >= (3, 10):  # pragma: no cover
+    from types import EllipsisType as InferType
+
+else:
+    InferType = type(Ellipsis)
 
 
 class HypothesisTestCase:
@@ -84,7 +90,7 @@ def from_model(
         shop_strategy = from_model(Shop, company=from_model(Company))
 
     Like for :func:`~hypothesis.strategies.builds`, you can pass
-    :obj:`~hypothesis.infer` as a keyword argument to infer a strategy for
+    ``...`` (:obj:`python:Ellipsis`) as a keyword argument to infer a strategy for
     a field which has a default value instead of using the default.
     """
     if len(model) == 1:
@@ -171,7 +177,7 @@ def from_form(
         shop_strategy = from_form(Shop, form_kwargs={"company_id": 5})
 
     Like for :func:`~hypothesis.strategies.builds`, you can pass
-    :obj:`~hypothesis.infer` as a keyword argument to infer a strategy for
+    ``...`` (:obj:`python:Ellipsis`) as a keyword argument to infer a strategy for
     a field which has a default value instead of using the default.
     """
     # currently unsupported:
diff --git a/hypothesis-python/src/hypothesis/extra/ghostwriter.py b/hypothesis-python/src/hypothesis/extra/ghostwriter.py
index 94bcd79133..4aa25b0e80 100644
--- a/hypothesis-python/src/hypothesis/extra/ghostwriter.py
+++ b/hypothesis-python/src/hypothesis/extra/ghostwriter.py
@@ -111,7 +111,14 @@
     SampledFromStrategy,
 )
 from hypothesis.strategies._internal.types import _global_type_lookup, is_generic_type
-from hypothesis.utils.conventions import InferType, infer
+from hypothesis.utils.conventions import infer
+
+if sys.version_info >= (3, 10):  # pragma: no cover
+    from types import EllipsisType as InferType
+
+else:
+    InferType = type(Ellipsis)
+
 
 IMPORT_SECTION = """
 # This test code was written by the `hypothesis.extra.ghostwriter` module
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/attrs.py b/hypothesis-python/src/hypothesis/strategies/_internal/attrs.py
index 0c8477231b..d4f56a1f3a 100644
--- a/hypothesis-python/src/hypothesis/strategies/_internal/attrs.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/attrs.py
@@ -36,7 +36,7 @@ def from_attrs(target, args, kwargs, to_infer):
 def from_attrs_attribute(attrib, target):
     """Infer a strategy from the metadata on an attr.Attribute object."""
     # Try inferring from the default argument.  Note that this will only help if
-    # the user passed `infer` to builds() for this attribute, but in that case
+    # the user passed `...` to builds() for this attribute, but in that case
     # we use it as the minimal example.
     default = st.nothing()
     if isinstance(attrib.default, attr.Factory):
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/core.py b/hypothesis-python/src/hypothesis/strategies/_internal/core.py
index 96d9a81607..c8f4c58d47 100644
--- a/hypothesis-python/src/hypothesis/strategies/_internal/core.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/core.py
@@ -104,7 +104,14 @@
     TextStrategy,
 )
 from hypothesis.strategies._internal.utils import cacheable, defines_strategy
-from hypothesis.utils.conventions import InferType, infer, not_set
+from hypothesis.utils.conventions import infer, not_set
+
+if sys.version_info >= (3, 10):  # pragma: no cover
+    from types import EllipsisType as InferType
+
+else:
+    InferType = type(Ellipsis)
+
 
 try:
     from typing import Protocol
@@ -837,9 +844,9 @@ def builds(
 
     If the callable has type annotations, they will be used to infer a strategy
     for required arguments that were not passed to builds.  You can also tell
-    builds to infer a strategy for an optional argument by passing the special
-    value :const:`hypothesis.infer` as a keyword argument to
-    builds, instead of a strategy for that argument to the callable.
+    builds to infer a strategy for an optional argument by passing ``...``
+    (:obj:`python:Ellipsis`) as a keyword argument to builds, instead of a strategy for
+    that argument to the callable.
 
     If the callable is a class defined with :pypi:`attrs`, missing required
     arguments will be inferred from the attribute on a best-effort basis,
@@ -864,7 +871,7 @@ def builds(
     if infer in args:
         # Avoid an implementation nightmare juggling tuples and worse things
         raise InvalidArgument(
-            "infer was passed as a positional argument to "
+            "... was passed as a positional argument to "
             "builds(), but is only allowed as a keyword arg"
         )
     required = required_args(target, args, kwargs)
@@ -880,7 +887,7 @@ def builds(
         if to_infer - set(hints):
             badargs = ", ".join(sorted(to_infer - set(hints)))
             raise InvalidArgument(
-                f"passed infer for {badargs}, but there is no type annotation"
+                f"passed ... for {badargs}, but there is no type annotation"
             )
         infer_for = {k: v for k, v in hints.items() if k in (required | to_infer)}
         if infer_for:
@@ -1860,7 +1867,7 @@ def functions(
 ) -> SearchStrategy[Callable[..., Any]]:
     # The proper type signature of `functions()` would have T instead of Any, but mypy
     # disallows default args for generics: https://github.com/python/mypy/issues/3737
-    """functions(*, like=lambda: None, returns=infer, pure=False)
+    """functions(*, like=lambda: None, returns=..., pure=False)
 
     A strategy for functions, which can be used in callbacks.
 
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/datetime.py b/hypothesis-python/src/hypothesis/strategies/_internal/datetime.py
index 62b6a0e30c..dcc3c74b56 100644
--- a/hypothesis-python/src/hypothesis/strategies/_internal/datetime.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/datetime.py
@@ -29,11 +29,11 @@
     import zoneinfo
 except ImportError:
     try:
-        from backports import zoneinfo
+        from backports import zoneinfo  # type: ignore
     except ImportError:
         # We raise an error recommending `pip install hypothesis[zoneinfo]`
         # when timezones() or timezone_keys() strategies are actually used.
-        zoneinfo = None
+        zoneinfo = None  # type: ignore
 
 DATENAMES = ("year", "month", "day")
 TIMENAMES = ("hour", "minute", "second", "microsecond")
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/types.py b/hypothesis-python/src/hypothesis/strategies/_internal/types.py
index bec4a889e4..7491d009ea 100644
--- a/hypothesis-python/src/hypothesis/strategies/_internal/types.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/types.py
@@ -59,7 +59,7 @@
 
 TypeAliasTypes: tuple = ()
 try:
-    TypeAliasTypes += (typing.TypeAlias,)  # type: ignore
+    TypeAliasTypes += (typing.TypeAlias,)
 except AttributeError:
     pass  # Is missing for `python<3.10`
 try:
@@ -75,7 +75,7 @@
 
 FinalTypes: tuple = ()
 try:
-    FinalTypes += (typing.Final,)  # type: ignore
+    FinalTypes += (typing.Final,)
 except AttributeError:  # pragma: no cover
     pass  # Is missing for `python<3.8`
 try:
@@ -577,7 +577,7 @@ def _networks(bits):
     }
 )
 if hasattr(typing, "SupportsIndex"):  # pragma: no branch  # new in Python 3.8
-    _global_type_lookup[typing.SupportsIndex] = st.integers() | st.booleans()  # type: ignore
+    _global_type_lookup[typing.SupportsIndex] = st.integers() | st.booleans()
 
 
 def register(type_, fallback=None, *, module=typing):
diff --git a/hypothesis-python/src/hypothesis/utils/conventions.py b/hypothesis-python/src/hypothesis/utils/conventions.py
index ae02443dbd..ec01326b49 100644
--- a/hypothesis-python/src/hypothesis/utils/conventions.py
+++ b/hypothesis-python/src/hypothesis/utils/conventions.py
@@ -19,9 +19,5 @@ def __repr__(self):
         return self.identifier
 
 
-class InferType(UniqueIdentifier):
-    """We have a subclass for `infer` so we can type-hint public APIs."""
-
-
-infer = InferType("infer")
+infer = ...
 not_set = UniqueIdentifier("not_set")
diff --git a/hypothesis-python/tests/cover/test_attrs_inference.py b/hypothesis-python/tests/cover/test_attrs_inference.py
index 724585938c..e4fe7dca74 100644
--- a/hypothesis-python/tests/cover/test_attrs_inference.py
+++ b/hypothesis-python/tests/cover/test_attrs_inference.py
@@ -13,7 +13,7 @@
 import attr
 import pytest
 
-from hypothesis import given, infer, strategies as st
+from hypothesis import given, strategies as st
 from hypothesis.errors import ResolutionFailed
 
 
@@ -70,7 +70,7 @@ class UnhelpfulConverter:
     a = attr.ib(converter=lambda x: x)
 
 
-@given(st.builds(Inferrables, has_default=infer, has_default_factory=infer))
+@given(st.builds(Inferrables, has_default=..., has_default_factory=...))
 def test_attrs_inference_builds(c):
     pass
 
@@ -88,4 +88,4 @@ def test_cannot_infer(c):
 
 def test_cannot_infer_takes_self():
     with pytest.raises(ResolutionFailed):
-        st.builds(Inferrables, has_default_factory_takes_self=infer).example()
+        st.builds(Inferrables, has_default_factory_takes_self=...).example()
diff --git a/hypothesis-python/tests/cover/test_functions.py b/hypothesis-python/tests/cover/test_functions.py
index d4f76e5e54..369aeebb41 100644
--- a/hypothesis-python/tests/cover/test_functions.py
+++ b/hypothesis-python/tests/cover/test_functions.py
@@ -12,7 +12,7 @@
 
 import pytest
 
-from hypothesis import assume, given, infer
+from hypothesis import assume, given
 from hypothesis.errors import InvalidArgument, InvalidState
 from hypothesis.strategies import booleans, functions, integers
 
@@ -112,7 +112,7 @@ def test_can_call_default_like_arg():
     # branch for calling it otherwise and alternative workarounds are worse.
     defaults = getfullargspec(functions).kwonlydefaults
     assert defaults["like"]() is None
-    assert defaults["returns"] is infer
+    assert defaults["returns"] is ...
 
 
 def func(arg, *, kwonly_arg):
diff --git a/hypothesis-python/tests/cover/test_given_error_conditions.py b/hypothesis-python/tests/cover/test_given_error_conditions.py
index 4635605b8b..cb6338c47f 100644
--- a/hypothesis-python/tests/cover/test_given_error_conditions.py
+++ b/hypothesis-python/tests/cover/test_given_error_conditions.py
@@ -10,7 +10,7 @@
 
 import pytest
 
-from hypothesis import assume, given, infer, reject, settings
+from hypothesis import assume, given, reject, settings
 from hypothesis.errors import InvalidArgument, Unsatisfiable
 from hypothesis.strategies import booleans, integers
 
@@ -35,7 +35,16 @@ def test_assume_x(x):
 
 
 def test_error_if_has_no_hints():
-    @given(a=infer)
+    @given(a=...)
+    def inner(a):
+        pass
+
+    with pytest.raises(InvalidArgument):
+        inner()
+
+
+def test_error_if_infer_all_and_has_no_hints():
+    @given(...)
     def inner(a):
         pass
 
@@ -44,8 +53,17 @@ def inner(a):
 
 
 def test_error_if_infer_is_posarg():
-    @given(infer)
-    def inner(ex):
+    @given(..., ...)
+    def inner(ex1: int, ex2: int):
+        pass
+
+    with pytest.raises(InvalidArgument):
+        inner()
+
+
+def test_error_if_infer_is_posarg_mixed_with_kwarg():
+    @given(..., ex2=...)
+    def inner(ex1: int, ex2: int):
         pass
 
     with pytest.raises(InvalidArgument):
diff --git a/hypothesis-python/tests/cover/test_lookup.py b/hypothesis-python/tests/cover/test_lookup.py
index 3e89343aba..ec88c77ec2 100644
--- a/hypothesis-python/tests/cover/test_lookup.py
+++ b/hypothesis-python/tests/cover/test_lookup.py
@@ -23,7 +23,7 @@
 
 import pytest
 
-from hypothesis import HealthCheck, assume, given, infer, settings, strategies as st
+from hypothesis import HealthCheck, assume, given, settings, strategies as st
 from hypothesis.errors import InvalidArgument, ResolutionFailed
 from hypothesis.internal.compat import get_type_hints
 from hypothesis.internal.reflection import get_pretty_function_description
@@ -189,7 +189,7 @@ def test_regex_types(data, generic, typ):
     assert isinstance(x[0] if generic is typing.Match else x.pattern, typ)
 
 
-@given(x=infer)
+@given(x=...)
 def test_Generator(x: typing.Generator[Elem, None, ElemValue]):
     assert isinstance(x, typing.Generator)
     try:
@@ -383,7 +383,7 @@ def test_force_builds_to_infer_strategies_for_default_args():
     # By default, leaves args with defaults and minimises to 2+4=6
     assert minimal(st.builds(annotated_func), lambda ex: True) == 6
     # Inferring integers() for args makes it minimise to zero
-    assert minimal(st.builds(annotated_func, b=infer, d=infer), lambda ex: True) == 0
+    assert minimal(st.builds(annotated_func, b=..., d=...), lambda ex: True) == 0
 
 
 def non_annotated_func(a, b=2, *, c, d=4):
@@ -392,14 +392,14 @@ def non_annotated_func(a, b=2, *, c, d=4):
 
 def test_cannot_pass_infer_as_posarg():
     with pytest.raises(InvalidArgument):
-        st.builds(annotated_func, infer).example()
+        st.builds(annotated_func, ...).example()
 
 
 def test_cannot_force_inference_for_unannotated_arg():
     with pytest.raises(InvalidArgument):
-        st.builds(non_annotated_func, a=infer, c=st.none()).example()
+        st.builds(non_annotated_func, a=..., c=st.none()).example()
     with pytest.raises(InvalidArgument):
-        st.builds(non_annotated_func, a=st.none(), c=infer).example()
+        st.builds(non_annotated_func, a=st.none(), c=...).example()
 
 
 class UnknownType:
@@ -425,16 +425,16 @@ def test_raises_for_arg_with_unresolvable_annotation():
     with pytest.raises(ResolutionFailed):
         st.builds(unknown_annotated_func).example()
     with pytest.raises(ResolutionFailed):
-        st.builds(unknown_annotated_func, a=st.none(), c=infer).example()
+        st.builds(unknown_annotated_func, a=st.none(), c=...).example()
 
 
-@given(a=infer, b=infer)
+@given(a=..., b=...)
 def test_can_use_type_hints(a: int, b: float):
     assert isinstance(a, int) and isinstance(b, float)
 
 
 def test_error_if_has_unresolvable_hints():
-    @given(a=infer)
+    @given(a=...)
     def inner(a: UnknownType):
         pass
 
@@ -761,7 +761,7 @@ def __init__(self, **kwargs):
 
 def test_compat_get_type_hints_aware_of_None_default():
     # Regression test for https://github.com/HypothesisWorks/hypothesis/issues/2648
-    strategy = st.builds(WithOptionalInSignature, a=infer)
+    strategy = st.builds(WithOptionalInSignature, a=...)
     find_any(strategy, lambda x: x.a is None)
     find_any(strategy, lambda x: x.a is not None)
 
diff --git a/hypothesis-python/tests/cover/test_lookup_py37.py b/hypothesis-python/tests/cover/test_lookup_py37.py
index 7a95a204cc..6a1955eb6b 100644
--- a/hypothesis-python/tests/cover/test_lookup_py37.py
+++ b/hypothesis-python/tests/cover/test_lookup_py37.py
@@ -18,7 +18,7 @@
 
 import pytest
 
-from hypothesis import assume, given, infer
+from hypothesis import assume, given
 
 # On Python 3.7 and 3.8, `from __future__ import annotations` means
 # that the syntax is supported; but the feature fails at runtime.  On Python
@@ -46,48 +46,48 @@ def check(t, ex):
     assume(ex)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_tuple1_as_generic(x: tuple[Elem]):
     check(tuple, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_tuple2_as_generic(x: tuple[Elem, Elem]):
     check(tuple, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_tuple_variadic_as_generic(x: tuple[Elem, ...]):
     check(tuple, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_list_as_generic(x: list[Elem]):
     check(list, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_dict_as_generic(x: dict[Elem, Value]):
     check(dict, x)
     assert all(isinstance(e, Value) for e in x.values())
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_set_as_generic(x: set[Elem]):
     check(set, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_frozenset_as_generic(x: frozenset[Elem]):
     check(frozenset, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_deque_as_generic(x: collections.deque[Elem]):
     check(collections.deque, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_defaultdict_as_generic(
     x: collections.defaultdict[Elem, Value]
 ):
@@ -95,7 +95,7 @@ def test_resolving_standard_defaultdict_as_generic(
     assert all(isinstance(e, Value) for e in x.values())
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_ordered_dict_as_generic(
     x: collections.OrderedDict[Elem, Value]
 ):
@@ -103,29 +103,29 @@ def test_resolving_standard_ordered_dict_as_generic(
     assert all(isinstance(e, Value) for e in x.values())
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_counter_as_generic(x: collections.Counter[Elem]):
     check(collections.Counter, x)
     assume(any(x.values()))  # Check that we generated at least one nonzero count
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_chainmap_as_generic(x: collections.ChainMap[Elem, Value]):
     check(collections.ChainMap, x)
     assert all(isinstance(e, Value) for e in x.values())
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_iterable_as_generic(x: collections.abc.Iterable[Elem]):
     check(collections.abc.Iterable, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_iterator_as_generic(x: collections.abc.Iterator[Elem]):
     check(collections.abc.Iterator, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_generator_as_generic(
     x: collections.abc.Generator[Elem, None, Value]
 ):
@@ -139,22 +139,22 @@ def test_resolving_standard_generator_as_generic(
         assert isinstance(stop.value, Value)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_reversible_as_generic(x: collections.abc.Reversible[Elem]):
     check(collections.abc.Reversible, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_container_as_generic(x: collections.abc.Container[Elem]):
     check(collections.abc.Container, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_collection_as_generic(x: collections.abc.Collection[Elem]):
     check(collections.abc.Collection, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_callable_ellipsis(x: collections.abc.Callable[..., Elem]):
     assert isinstance(x, collections.abc.Callable)
     assert callable(x)
@@ -163,7 +163,7 @@ def test_resolving_standard_callable_ellipsis(x: collections.abc.Callable[..., E
     assert isinstance(x(1, 2, 3, a=4, b=5, c=6), Elem)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_callable_no_args(x: collections.abc.Callable[[], Elem]):
     assert isinstance(x, collections.abc.Callable)
     assert callable(x)
@@ -175,25 +175,25 @@ def test_resolving_standard_callable_no_args(x: collections.abc.Callable[[], Ele
         x(a=1)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_collections_set_as_generic(x: collections.abc.Set[Elem]):
     check(collections.abc.Set, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_collections_mutableset_as_generic(
     x: collections.abc.MutableSet[Elem],
 ):
     check(collections.abc.MutableSet, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_mapping_as_generic(x: collections.abc.Mapping[Elem, Value]):
     check(collections.abc.Mapping, x)
     assert all(isinstance(e, Value) for e in x.values())
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_mutable_mapping_as_generic(
     x: collections.abc.MutableMapping[Elem, Value],
 ):
@@ -201,24 +201,24 @@ def test_resolving_standard_mutable_mapping_as_generic(
     assert all(isinstance(e, Value) for e in x.values())
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_sequence_as_generic(x: collections.abc.Sequence[Elem]):
     check(collections.abc.Sequence, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_mutable_sequence_as_generic(
     x: collections.abc.MutableSequence[Elem],
 ):
     check(collections.abc.MutableSequence, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_keysview_as_generic(x: collections.abc.KeysView[Elem]):
     check(collections.abc.KeysView, x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_itemsview_as_generic(
     x: collections.abc.ItemsView[Elem, Value]
 ):
@@ -227,38 +227,38 @@ def test_resolving_standard_itemsview_as_generic(
     assume(x)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_valuesview_as_generic(x: collections.abc.ValuesView[Elem]):
     check(collections.abc.ValuesView, x)
 
 
 @pytest.mark.xfail  # Weird interaction with fixes in PR #2952
-@given(x=infer)
+@given(...)
 def test_resolving_standard_contextmanager_as_generic(
     x: contextlib.AbstractContextManager[Elem],
 ):
     assert isinstance(x, contextlib.AbstractContextManager)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_re_match_bytes_as_generic(x: re.Match[bytes]):
     assert isinstance(x, re.Match)
     assert isinstance(x[0], bytes)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_re_match_str_as_generic(x: re.Match[str]):
     assert isinstance(x, re.Match)
     assert isinstance(x[0], str)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_re_pattern_bytes_as_generic(x: re.Pattern[bytes]):
     assert isinstance(x, re.Pattern)
     assert isinstance(x.pattern, bytes)
 
 
-@given(x=infer)
+@given(...)
 def test_resolving_standard_re_pattern_str_as_generic(x: re.Pattern[str]):
     assert isinstance(x, re.Pattern)
     assert isinstance(x.pattern, str)
diff --git a/hypothesis-python/tests/cover/test_type_lookup.py b/hypothesis-python/tests/cover/test_type_lookup.py
index 4697c613cb..d14251cd30 100644
--- a/hypothesis-python/tests/cover/test_type_lookup.py
+++ b/hypothesis-python/tests/cover/test_type_lookup.py
@@ -14,7 +14,7 @@
 
 import pytest
 
-from hypothesis import given, infer, strategies as st
+from hypothesis import given, infer, settings, strategies as st
 from hypothesis.errors import (
     HypothesisDeprecationWarning,
     InvalidArgument,
@@ -172,13 +172,14 @@ def test_pulic_interface_works():
         fails.example()
 
 
-def test_given_can_infer_from_manual_annotations():
+@pytest.mark.parametrize("infer_token", [infer, ...])
+def test_given_can_infer_from_manual_annotations(infer_token):
     # Editing annotations before decorating is hilariously awkward, but works!
     def inner(a):
-        pass
+        assert isinstance(a, int)
 
     inner.__annotations__ = {"a": int}
-    given(a=infer)(inner)()
+    given(a=infer_token)(inner)()
 
 
 class EmptyEnum(enum.Enum):
@@ -391,3 +392,27 @@ def test_abstract_resolver_fallback():
 
     # which in turn means we resolve to the concrete subtype.
     assert isinstance(gen, ConcreteBar)
+
+
+def _one_arg(x: int):
+    assert isinstance(x, int)
+
+
+def _multi_arg(x: int, y: str):
+    assert isinstance(x, int)
+    assert isinstance(y, str)
+
+
+def _kwd_only(*, y: str):
+    assert isinstance(y, str)
+
+
+def _pos_and_kwd_only(x: int, *, y: str):
+    assert isinstance(x, int)
+    assert isinstance(y, str)
+
+
+@pytest.mark.parametrize("func", [_one_arg, _multi_arg, _kwd_only, _pos_and_kwd_only])
+def test_infer_all(func):
+    # tests @given(...) against various signatures
+    settings(max_examples=1)(given(...))(func)()
diff --git a/hypothesis-python/tests/django/toystore/test_given_models.py b/hypothesis-python/tests/django/toystore/test_given_models.py
index a66ea45c59..ac21b0355a 100644
--- a/hypothesis-python/tests/django/toystore/test_given_models.py
+++ b/hypothesis-python/tests/django/toystore/test_given_models.py
@@ -15,7 +15,7 @@
 from django.conf import settings as django_settings
 from django.contrib.auth.models import User
 
-from hypothesis import HealthCheck, assume, given, infer, settings
+from hypothesis import HealthCheck, assume, given, settings
 from hypothesis.control import reject
 from hypothesis.errors import HypothesisException, InvalidArgument
 from hypothesis.extra.django import (
@@ -111,11 +111,11 @@ def test_mandatory_computed_fields_may_not_be_provided(self):
         mc = from_model(MandatoryComputed, company=from_model(Company))
         self.assertRaises(RuntimeError, mc.example)
 
-    @given(from_model(CustomishDefault, customish=infer))
+    @given(from_model(CustomishDefault, customish=...))
     def test_customish_default_overridden_by_infer(self, x):
         assert x.customish == "a"
 
-    @given(from_model(CustomishDefault, customish=infer))
+    @given(from_model(CustomishDefault, customish=...))
     def test_customish_infer_uses_registered_instead_of_default(self, x):
         assert x.customish == "a"
 
diff --git a/mypy.ini b/mypy.ini
index 663a902102..f92749dc14 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -1,5 +1,5 @@
 [mypy]
-python_version = 3.7
+python_version = 3.10
 platform = linux
 
 disallow_untyped_decorators = True


Code review comments:
diff_hunk: @@ -887,7 +887,7 @@ def builds(
         if to_infer - set(hints):
             badargs = ", ".join(sorted(to_infer - set(hints)))
             raise InvalidArgument(
-                f"passed infer for {badargs}, but there is no type annotation"
+                f"passed ... for {badargs}, but there is no type annotation"
path: hypothesis-python/src/hypothesis/strategies/_internal/core.py
text: @user1:
Without the word "infer" this might be a confusing message - how about "but we cannot infer a strategy because there is no type annotation"?