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

Improve error message when st.builds() is missing required uninferrable arguments #3225

Closed
sobolevn opened this issue Jan 30, 2022 · 1 comment · Fixed by #3250
Closed

Improve error message when st.builds() is missing required uninferrable arguments #3225

sobolevn opened this issue Jan 30, 2022 · 1 comment · Fixed by #3250
Labels
legibility make errors helpful and Hypothesis grokable

Comments

@sobolevn
Copy link
Member

sobolevn commented Jan 30, 2022

Right now hypothesis ignores @no_type_check decorator applied to functions and types.

For example:

from hypothesis import strategies as st, given

class NotTypes:
    def __init__(self, arg: str) -> None:
        self.arg = arg

@given(st.builds(NotTypes))
def test_no_type_check(instance):
    assert isinstance(instance, NotTypes)

This passes as expected. But, consider this:

from hypothesis import strategies as st, given
from typing import no_type_check

@no_type_check
class NotTypes:
    def __init__(self, arg: str) -> None:
        self.arg = arg

@given(st.builds(NotTypes))
def test_no_type_check(instance):
    assert isinstance(instance, NotTypes)

This example fails with unclear error:

_________________________________ test_no_type_check _________________________________

    @given(st.builds(NotTypes))
>   def test_no_type_check(instance):

ex.py:10: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/engine.py:470: in run
    self._run()
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/engine.py:870: in _run
    self.reuse_existing_examples()
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/engine.py:548: in reuse_existing_examples
    data = self.cached_test_function(existing)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/engine.py:1051: in cached_test_function
    self.test_function(data)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/engine.py:208: in test_function
    self.__stoppable_test_function(data)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/engine.py:184: in __stoppable_test_function
    self._test_function(data)
.venv/lib/python3.9/site-packages/hypothesis/core.py:725: in _execute_once_for_engine
    escalate_hypothesis_internal_error()
.venv/lib/python3.9/site-packages/hypothesis/core.py:699: in _execute_once_for_engine
    result = self.execute_once(data)
.venv/lib/python3.9/site-packages/hypothesis/core.py:637: in execute_once
    result = self.test_runner(data, run)
.venv/lib/python3.9/site-packages/hypothesis/executors.py:47: in default_new_style_executor
    return function(data)
.venv/lib/python3.9/site-packages/hypothesis/core.py:596: in run
    args, kwargs = data.draw(self.search_strategy)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:878: in draw
    return strategy.do_draw(self)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/collections.py:58: in do_draw
    return tuple(data.draw(e) for e in self.element_strategies)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/collections.py:58: in <genexpr>
    return tuple(data.draw(e) for e in self.element_strategies)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:874: in draw
    return strategy.do_draw(self)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/strategies.py:818: in do_draw
    result = self.pack(data.draw(self.mapped_strategy))
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:874: in draw
    return strategy.do_draw(self)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/lazy.py:164: in do_draw
    return data.draw(self.wrapped_strategy)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:874: in draw
    return strategy.do_draw(self)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/strategies.py:818: in do_draw
    result = self.pack(data.draw(self.mapped_strategy))
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:874: in draw
    return strategy.do_draw(self)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/collections.py:58: in do_draw
    return tuple(data.draw(e) for e in self.element_strategies)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/collections.py:58: in <genexpr>
    return tuple(data.draw(e) for e in self.element_strategies)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:874: in draw
    return strategy.do_draw(self)
.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/lazy.py:164: in do_draw
    return data.draw(self.wrapped_strategy)
.venv/lib/python3.9/site-packages/hypothesis/internal/conjecture/data.py:874: in draw
    return strategy.do_draw(self)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = builds(NotTypes), data = ConjectureData(VALID, 0 bytes, frozen)

    def do_draw(self, data):
        try:
>           return self.target(
                *(data.draw(a) for a in self.args),
                **{k: data.draw(v) for k, v in self.kwargs.items()},
            )
E           TypeError: __init__() missing 1 required positional argument: 'arg'

.venv/lib/python3.9/site-packages/hypothesis/strategies/_internal/core.py:777: TypeError

I think it happens because get_type_hints return {} for types like NotTypes.
That's why we can't create a correct amount of arguments for __init__.

Probably, we can at least improve the error message to soething like:

Auto resolving types marked with `@no_type_check` is not supported. Explicit `register_type_strategy()` is required.

What do you think?

Context: https://bugs.python.org/issue46571

@Zac-HD
Copy link
Member

Zac-HD commented Jan 30, 2022

This isn't specific to @no_type_check; it's a generic error message for any case where st.builds() is missing a strategy for a required unannotated argument.

I think it makes sense here to catch TypeError, check if our args and kwargs can't satisfy the required args for the target, and if so raise a TypeError(...) from None with a much more specific error message. That message might or might not have a variant for "you used @no_type_check" 🙂

@Zac-HD Zac-HD added the legibility make errors helpful and Hypothesis grokable label Jan 30, 2022
@Zac-HD Zac-HD changed the title Support @no_type_check decorator Improve error message when st.builds() is missing required uninferrable arguments Jan 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
legibility make errors helpful and Hypothesis grokable
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants