Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generation doesn't work with Annotated types #2919

Closed
vstrimaitis opened this issue Mar 31, 2021 · 3 comments · Fixed by #2920
Closed

Generation doesn't work with Annotated types #2919

vstrimaitis opened this issue Mar 31, 2021 · 3 comments · Fixed by #2920
Labels
bug something is clearly wrong here

Comments

@vstrimaitis
Copy link
Contributor

vstrimaitis commented Mar 31, 2021

Versions

python==3.7.7
hypothesis==6.8.3
typing-extensions==3.7.4.3

Also tested with python==3.9.0 and using typing.Annotated instead of typing_extensions.Annotated.

Problem description

hypothesis is unable to generate data for a type if Annotated is added. Example:

import hypothesis.strategies as st
from hypothesis import given
from typing_extensions import Annotated

# @given(st.from_type(int))                  # <-- this works
@given(st.from_type(Annotated[int, "foo"]))  # <-- this doesn't
def test(s):
    print(s)

test()

The error that I receive is as follows:

hypothesis.errors.ResolutionFailed: Could not resolve typing.Annotated[int, 'foo'] to a strategy; consider using register_type_strategy

I tried registering a strategy as the error message suggests like so:

def my_strategy(t):
    raise Exception("Should enter here")

st.register_type_strategy(Annotated, my_strategy)

# ... rest of the example from above

... but the my_strategy function is never called, and the example fails with the same error.

Potential solution

I followed the traceback to the function that failed and discovered this piece of code:

origin = getattr(thing, "__origin__", thing)
if (
typing.Hashable is not collections.abc.Hashable
and origin in vars(collections.abc).values()
and len(getattr(thing, "__args__", None) or []) == 0
):
return st.from_type(origin)

As far as I've tested, Annotated[int, "foo"].__origin__ returns int, but len(Annotated[int, "foo"].__args__) is 1. I guess adding an extra condition here to check for the case of Annotated would fix the issue, but I'm not familiar with the codebase, so correct me if I'm wrong.

I found no workarounds for the issue, so any suggestions are highly appreciated!

@Zac-HD Zac-HD added the bug something is clearly wrong here label Mar 31, 2021
@Zac-HD
Copy link
Member

Zac-HD commented Mar 31, 2021

I think adding a clause for Annotated here would indeed fix your problem.

However, we might want to have a separate clause like if is_annotated_type(thing): just after is_typing_literal(), because we have both typing and typing_extensions, plus complications like type(typing_extensions.Annotated[int, "label"]) == typing_extensions._AnnotatedAlias which can probably vary with Python version.

(this would also make it easier for a future PR to support e.g. Annotated[float, lambda x: 0 <= x < 1], and resolve this to st.floats().filter(lambda x: 0 <= x < 1) with a plan (#2701) for st.floats(0, 1, exclude_max=True)... at least if we can convince people that this is the natural interpretation of callable predicates for annotated types)


Are you interested in writing a PR for this one?

@vstrimaitis
Copy link
Contributor Author

Sure, I can take a stab at it. Could you just clarify this part?

we might want to have a separate clause like if is_typing_literal(thing): just after is_typing_literal()

I mean did you intentionally leave the name is_typing_literal in both places here? 😁

@Zac-HD
Copy link
Member

Zac-HD commented Mar 31, 2021

Ahem, that's is_annotated_type() 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something is clearly wrong here
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants