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

[red-knot] improve type shrinking coverage in red-knot property tests #15297

Merged
merged 1 commit into from
Jan 7, 2025

Conversation

rtpg
Copy link
Contributor

@rtpg rtpg commented Jan 6, 2025

Summary

While looking at #14899, I looked at seeing if I could get shrinking on the examples. It turned out to be straightforward, with a couple of caveats.

I'm calling clone a lot during shrinking. Since by the shrink step we're already looking at a test failure this feels fine? Unless I misunderstood quickcheck's core loop

When shrinking Intersections, in order to just rely on quickcheck's Vec shrinking without thinking about it too much, the shrinking strategy is:

  • try to shrink the negative side (keeping the positive side the same)
  • try to shrink the positive side (keeping the negative side the same)

This means that you can't shrink from (A & B & ~C & ~D) directly to (A & ~C)! You would first need an intermediate failure at (A & B & ~C) or (A & ~C & ~D). This feels good enough. Shrinking the negative side first also has the benefit of trying to strip down negative elements in these intersections.

Test Plan

cargo test -p red_knot_python_semantic -- --ignored types::property_tests::stable still fails as it current does on main, but now the errors seem more minimal.

Copy link
Contributor

github-actions bot commented Jan 6, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

@MichaReiser MichaReiser added testing Related to testing Ruff itself red-knot Multi-file analysis & type inference labels Jan 6, 2025
@rtpg
Copy link
Contributor Author

rtpg commented Jan 6, 2025

I believe to have handled all the comments in the first review and have pushed up a new version

Copy link
Contributor

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for working on this!

If it's not too much effort, it would be great to see some evidence that shrinking actually gets better. Is it possible to pin the random seed in quickcheck and then show a few before/after results?

Ty::Tuple(types) => Box::new(types.into_iter()),
Ty::Intersection { pos, neg } => Box::new(pos.into_iter().chain(neg)),
Ty::Union(types) => Box::new(types.shrink().filter_map(|elts| match elts.len() {
0 => None,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might not be completely unreasonable to try and return Ty::Never here?

This also makes me think: I think we currently only try shrinking for the root of the Type tree? Like if we have tuple[A | B, C], I think we would only try to remove either A | B from the tuple, or try to remove C from the tuple, i.e. we try to shrink to C or to A | B, but we would not try to shrink the A | B union.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling shrink on the vector of types actually does attempt to shrink the elements of the vector as well as trying to remove elements, so we get nice shrinking just leaning on the shrinking built into quickcheck (at least that was my read of the source)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the empty union shrinking to Ty::Never... feels fine by me! Especially now that I fully grok that Ty is a smart constructor for Type so I don't need to worried about types being in the "wrong location", so to speak

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling shrink on the vector of types actually does attempt to shrink the elements of the vector as well as trying to remove elements

👍

Regarding the empty union shrinking to Ty::Never... feels fine by me! Especially now that I fully grok that Ty is a smart constructor for Type so I don't need to worried about types being in the "wrong location", so to speak

If we try to shrink at all "depths", I think we can skip this. The idea was to simplify nested Ty unions like A | B | (C | D) to A | B | Never and then further down to A | B, but there are many other possible shrinking-paths that would lead to that result (e.g. via the single-element union or via a element-removal shrink on the outer union).

@rtpg
Copy link
Contributor Author

rtpg commented Jan 6, 2025

@sharkdp do you know how to pin the quickcheck seed? I'm looking through the quickcheck source and readme and not seeing anything, but I am famously bad at finding things that are obvious.

@sharkdp
Copy link
Contributor

sharkdp commented Jan 7, 2025

@sharkdp do you know how to pin the quickcheck seed?

No, I just hoped there would be a way. But it does not seem to be the case: BurntSushi/quickcheck#278

In this case, I'm okay with your "now the errors seem more minimal" observation.

Copy link
Contributor

@sharkdp sharkdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this.

@sharkdp sharkdp merged commit 066239f into astral-sh:main Jan 7, 2025
21 checks passed
dcreager added a commit that referenced this pull request Jan 7, 2025
* main:
  Use uv consistently throughout the documentation (#15302)
  [red-knot] Eagerly normalize `type[]` types (#15272)
  [`pyupgrade`] Split `UP007` to two individual rules for `Union` and `Optional` (`UP007`, `UP045`) (#15313)
  [red-knot] Improve symbol-lookup tracing (#14907)
  [red-knot] improve type shrinking coverage in red-knot property tests (#15297)
  [`flake8-return`] Recognize functions returning `Never` as non-returning (`RET503`) (#15298)
  [`flake8-bugbear`] Implement `class-as-data-structure` (`B903`) (#9601)
  Avoid treating newline-separated sections as sub-sections (#15311)
  Remove call when removing final argument from `format` (#15309)
  Don't enforce `object-without-hash-method` in stubs (#15310)
  Don't special-case class instances in binary expression inference (#15161)
  Upgrade zizmor to the latest version in CI (#15300)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
red-knot Multi-file analysis & type inference testing Related to testing Ruff itself
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants