-
Notifications
You must be signed in to change notification settings - Fork 587
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
Add type annotations to hypothesis/internal/conjecture/engine.py
#3961
Add type annotations to hypothesis/internal/conjecture/engine.py
#3961
Conversation
hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py
Outdated
Show resolved
Hide resolved
Wow! Thanks so much for contributing, this looks great 😀
No - these are specifically
Ah,
Looks like
Either of these sound like a good solution to me. |
Sorry for the delay in addressing comments. I've had a busy couple of days.
Thanks for this clarification. I've added a
Thanks for this clarification. I've gone ahead and moved the trial_data out of the try block.
Looks like
I went with a I believe I've addressed all comments at this point. Both |
@Zac-HD I don't think I understand what is failing in CI at the moment. Do you have any hints on what needs to be fixed? |
self.interesting_examples: Dict[ | ||
Optional[InterestingOrigin], ConjectureResult | ||
] = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is Dict[InterestingOrigin, ConjectureResult]
. data.interesting_origin
is optional, but we only add to this dict when it's nonnull.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typing this as Dict[InterestingOrigin, ConjectureResult]
, and then adding assert key is not None
before writing to this dictionary causes these tests to fail at that assert:
FAILED hypothesis-python/tests/conjecture/test_alt_backend.py::test_backend_can_shrink_bytes - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_stores_the_tree_flat_until_needed - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_split_in_the_middle - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_stores_forced_nodes - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_correctly_relocates_forced_nodes - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_is_not_flaky_on_positive_zero_and_negative_zero - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_low_probabilities_are_still_explored - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_can_generate_hard_values - AssertionError
FAILED hypothesis-python/tests/conjecture/test_data_tree.py::test_can_generate_hard_floats - AssertionError
FAILED hypothesis-python/tests/conjecture/test_engine.py::test_can_index_results - AssertionError
FAILED hypothesis-python/tests/conjecture/test_engine.py::test_non_cloneable_intervals - AssertionError
FAILED hypothesis-python/tests/conjecture/test_engine.py::test_deletable_draws - AssertionError
That's why I had typed it as Optional[InterestingOrigin]
. If we're certain the type should be Dict[InterestingOrigin, ConjectureResult]
, then a type-narrowing assert will cause the tests to fail. We could get around this with a type: ignore
on the lines that write to the dictionary, but I'm curious why so many tests fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, we've been abusing mark_interesting
in tests by not specifying an interesting origin. It's undeniably ergonomic but also means the type is more lenient than it needs to be in practice. I think we can leave a note here saying the type is InterestingOrigin
everywhere but tests, where it is Optional[InterestingOrigin]
for convenience, and leave cleaning this up for later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me! I left a comment # At runtime, the keys are only ever type
InterestingOrigin, but can be
None during tests.
above both this hint and self.shrunk_examples
that you mentioned below.
Should an issue be opened up to track making that change to the tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think it's worth it - we can define a simple InterestingOrigin
and paste it into all the tests, and then tightening up these annotations will help us work on the engine 🙂
I think some change in this PR probably caused this test to start failing, although I don't know why: hypothesis/hypothesis-python/tests/cover/test_composite.py Lines 197 to 206 in 8005910
#3968 is also failing in other PRs, so I'm confident that's not anything in this PR. |
No worries at all, you're not the only one! But more importantly, volunteer projects like Hypothesis are a labor of love and it's really important to me that they don't start to feel like an obligation. If you want to work on it, we're delighted to have you helping out! But if that changes, or if you'd just like a maintainer to finish polishing one PR so you can start on another, let us know and move on with our blessings 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This all looks great to me - thanks @qthequartermasterman!
The remaining CI failures are all from test_warns_on_strategy_annotation()
on Python 3.10 or 3.11 - and we already have a skipif-py39 on that due to the best-guess stack depth here. Probably we just need to add an additional case for the affected Python versions... great if you want to look into that, or I can try fixing it over the weekend.
self.interesting_examples: Dict[ | ||
Optional[InterestingOrigin], ConjectureResult | ||
] = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think it's worth it - we can define a simple InterestingOrigin
and paste it into all the tests, and then tightening up these annotations will help us work on the engine 🙂
92bab87
to
3f3377d
Compare
032daed
to
8b28c80
Compare
Thanks for finishing this out @Zac-HD ! |
Happy to help! It was a remarkably annoying merge conflict 😅 |
In an attempt to help with #3074, I have added type annotations to
hypothesis/internal/conjecture/engine.py
. I have checked these type hints with bothmypy
andpyright
, and verified a few of the trickier-to-statically-determine types by checking their runtime types in the tests.There are three things that I'm still confused about.
Overrun
be a validvalue
forself.interesting_examples
?In all places but this one sample (lines 389 in this PR), all of the keys for
self.interesting_examples
areConjectureResult
. However, this assignment may possibly beOverrun
. Indeed, in one test,test_backend_can_shrink_bytes
, this does occur. This seems (to me) to be the only place in the tests where thisOverrun
assignment occurs. IfOverrun
is a validvalue
, then this violates the typing in several other places whereself.interesting_examples
is accessed, but nothing seemed to ever trip up the asserts I had placed in those places (and since removed). IfOverrun
is indeed valid, then I will need to update the hints forself.interesting_examples
, and I'd like to better understand the guarantees that ensureOverrun
is never a value in other places (so the type narrowing asserts are legal). Many, but not all, of those references are inshrink_interesting_examples
for example.trial_data
because of a caughtPreviouslyUnseenBehavior
exceptionIn line 842,
trial_data
is defined inside of a try-except block. In the happy path, this variable is always defined. On the otherhand, unless I am misunderstanding something, if aPreviouslyUnseenBehavior
is thrown, that variable will not be defined, meaningtrial_data.observer.killed
and a few other later instances will reference an uninitialized variable. This never happens in the tests, and I've certainly never experienced it while usinghypothesis
.Am I misunderstanding the flow of the code here? Is this try-except superfluous? Or is there a genuine error in how the exception is handled?
stack
indebug_data
?In the
debug_data
method there is astack=[[]]
on line 587. The expected type forstack
is unclear to me. My initial impression based onstack[-1].append(int_from_bytes(data.buffer[ex.start : ex.end]))
is thatstack
should beList[List[int]]
. But these conditionals, wherenode:List[int]
give conflicting types.In the case of length-1 node, the final element of stack (which I thought is a
List[int]
) gets extended by one more integer. This makes sense to me. Theelse
case, however, would mean that the last element of stack would have a list of ints appended as its final element. I.e.stack[-1] == [1,2,3,..., [1,2,3,4]]
This means thatstack
would have to beList[List[int | List[int]]]
. But this type then starts tripping on variance issues.To further confuse matters, my debugger never seemed to stop on that strange
else
clause, so I never could actually find the proper runtime type.What is the correct type here? I'm clearly not understanding what the code is doing to this data structure.
On top of those three confusions I have,
mypy
also complains about some string literals as keys to a TypedDict (line 247), because it does not consider the concatenation of two literals to be a literal. See the code snippet below. Personally, I feel like this is an error inmypy
--pyright
does not have this complaint. If we would likemypy
to be fully happy, I can either add a# type: ignore
, or refactor the code to only use true literals.If we don't care that(I see now that mypy is enforced in CI. Happy to do whatever the maintainers recommend to satisfy mypy.)mypy
complains, then no change is needed.Doubtless, I've missed or misunderstood some pieces. I am happy to revise and iterate!