Various improvements in printing test cases#4694
Conversation
Liam-DeVoe
left a comment
There was a problem hiding this comment.
Many of these tests are cleanly written as @pytest.mark.parametrize, which will make reviewing this a lot easier! It's a lot of code right now.
Yeah, that's true, sorry. I'll get them cleaned up once I've got the failing tests I'd missed sorted.
FWIW I think there's fairly little non-test code. There's a degree of unavoidable code-quantity in getting reasonable snapshot coverage in place. I can split it out into multiple smaller stacked PRs if that would be helpful to you though. |
a7b4faf to
2823943
Compare
d308431 to
22ce429
Compare
When a lambda is used in builds() or map(), try to inline single-use arguments directly into the body expression. For example, (lambda b: hashlib.sha256(b).digest())(b'') becomes hashlib.sha256(b'').digest() Inlining is skipped when parameters are used more than once, when there are varargs/kwargs, when the argument repr isn't valid Python, or when explain-mode comments need the call-style repr. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Consolidate individual _inline() assertion tests into parametrized test_inline_success and test_inline_bail_out, and merge the individual _repr_call tests into the existing test_repr_call_parametrized. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
22ce429 to
b62162a
Compare
Liam-DeVoe
left a comment
There was a problem hiding this comment.
(first pass, I'd still like to review the logic of when to print as a call vs a repr in more detail)
|
|
||
| def _repr_pretty_(self, printer, cycle): | ||
| if cycle: | ||
| printer.text("iter(...)") | ||
| else: | ||
| printer.text("iter(") | ||
| printer.pretty(self._values) | ||
| printer.text(")") | ||
|
|
||
|
|
There was a problem hiding this comment.
I'm confused why _repr_pretty_ was added. It only differs from __repr__ when there's a cycle. But I'm not sure that is possible in the only place we use PrettyIter (st.iterables), and if it is then we should have a test for it!
There was a problem hiding this comment.
No, it differs from repr in a lot of places. e.g. line breaking of the list, if the values have their own pretty repr, etc.
I actually added it because __repr__ was getting skipped by the printing logic and _repr_pretty_ wasn't, but maybe that's no longer true with the new heuristics. It's possible this isn't actually needed to solve the problem I was originally solving, but I do think it solves a different class of important problems.
| try: | ||
| tree = ast.parse(func_name, mode="eval") | ||
| except SyntaxError: | ||
| return None |
There was a problem hiding this comment.
I've been bitten before by functions like ast.parse throwing all kinds of weird exceptions. For example, I wouldn't be surprised if ast.parse could throw RecursionError. I would prefer a broad except Exception here for safety.
| for repr_str in arg_reprs.values(): | ||
| try: | ||
| ast.parse(repr_str, mode="eval") | ||
| except SyntaxError: | ||
| return None |
There was a problem hiding this comment.
same here, I'd use except Exception
| ''' | ||
| Falsifying example: inner( | ||
| c=test_sampled_from_enum_flag.<locals>.Color.RED, | ||
| c=test_sampled_from_enum_flag.Color.RED, |
There was a problem hiding this comment.
I think this is better with .<locals>.
|
@Liam-DeVoe @Zac-HD How would you feel about replacing I'm not wed to it and happy to just pull this change. It's arguably worse to have python syntax that doesn't work than invalid python syntax. But I do think given how many of the heuristics in pretty printing check whether it's valid python source it would be good to have valid python source here if we could find some we were happy with. |
|
I don't like it. Making up syntax/methods which don't exist but is realistic enough that it plausibly could is the worst of both worlds. Can we change our checks for |
Hmm. That's not a bad idea. I think for now though I'm just going to pull it out of this PR. All the solutions seem weird and/or contentious and I think it probably rarely matters. |
Revert the <locals> stripping from get_class_name (both Zac and Liam prefer keeping it). Clean up _try_inline_lambda API to return bool instead of None/sentinel, and broaden except SyntaxError to except Exception for safety. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed mention of stripping invalid syntax from names.
#4692 made me suspicious so I went digging. I added snapshot tests using syrupy to validate Hypothesis's output on various tests and so we can catch regressions in it, and then I manually reviewed all the output and decided that a lot of it sucked, so I fixed it.
Principle things I fixed:
_repr_pretty_forPrettyItersoiterableshas good output instead of bad output.<locals>from names. This isn't a great solution, because it produces syntax that is valid (and unambiguously refers to the right thing) but won't actually successfully eval because that name is not accessible. I'm not sure there's anything much one can do about this though. I'm just noting that everything we can do here is sortof bad, and I think this is slightly less bad than the status quo (especially given that we have "is this valid syntax?" in a bunch of places in our pretty printing)."hello" + " " + "world"instead of(lambda x, y: x + " " + y)("hello", "world")or, the motivating example,hashlib.sha1(b'').digest()instead of(lambda b: hashlib.sha1(b).digest())(b'').Some of these changes it might be best to review commitwise because you can see the corresponding changes they make to the snapshots. In all cases I started with a snapshot of existing code, then made some changes to see how it updated the snapshot.