pythonmypypython-typingpython-hypothesis

Type hinting a Hypothesis composite strategy


I am using the hypothesis library and I would like to annotate my code with type hints. The docs are mentioning the hypothesis.strategies.SearchStrategy as the type for all search strategies.

Take this example:

@composite
def int_strategy(draw: DrawFn) -> hypothesis.strategies.SearchStrategy[int]:
    ... # some computation here resulting in ``x`` being an ``int``

    return x

Running mypy will (rightly so) result in an error along those lines: error: Returning Any from function declared to return "SearchStrategy[Any]" [no-any-return]

I mean, I am actually returning an int, not a SearchStrategy.

How am I supposed to type annotate my hypothesis strategies?


Solution

  • Functions decorated with @composite should be type hinted as normal:

    @composite
    def int_strategy(draw: DrawFn) -> int:
        ...
    

    @composite will then automatically transform this to something like:

    # As if it doesn't have the `draw` parameter and that it returns a `SearchStrategy`
    def int_strategy() -> SearchStrategy[int]:
        ...
    

    Don't believe me? Ask Mypy:

    # At call site
    reveal_type(int_strategy)    # () -> SearchStrategy[int]
    reveal_type(int_strategy())  # SearchStrategy[int]
    

    This is the same with other decorators: The eventual type of a function is determined by its original type hints and all of its @decorators'. In composite()'s case, this is how it is defined (at least at type-checking time):

    # Takes a function whose first argument is of type `DrawFn`
    # and returns a function without that argument,
    # returning a `SearchStrategy` that will output
    # values of the same type as the original's return type.
    def composite(
        f: Callable[Concatenate[DrawFn, P], Ex]
    ) -> Callable[P, SearchStrategy[Ex]]:
        ...
    

    In fact, using SearchStrategy[] as the return type is such a common mistake that the maintainers made it so that you would get a runtime warning:

    @composite
    def int_strategy() -> SearchStrategy[int]:
        ...
    
    tests/test_foo.py:6
      /project/tests/test_foo.py:6: HypothesisWarning: Return-type annotation is `st.SearchStrategy[int]`, but the decorated function should return a value (not a strategy)