-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Features assertion pass hook #3479
Features assertion pass hook #3479
Conversation
Hey @Sup3rGeo, thanks and sorry for the delay!
As an alternative, we should only activate this function if a certain config option is active, say I'm short on time right now, but I intend to take a closer look at the code soon. |
Quickly used pytest-ast-back-to-python to see the rewritten code for this test: def test_hook():
x = 1
y = {2: 3}
assert x == 1
|
@nicoddemus no problem, I am glad you are here to help! As you can see, the changes in the AST are relatively trivial. I guess the presence of the
This might work as well, but there could be any confusion if a plugin implements the hook and the config option is not active? |
@Sup3rGeo sorry for the delay on this. Do you still have interest on this feature? One of my concerns was the cost of always creating the explanation, but one way of solving that would be to pass a function to the hook that when called produces the explanation; it is weird in the sense that no hooks do that at the moment, but at least that would not incur a cost for normal usage. |
Hi @nicoddemus, no problem at all Yes I am still interested and I think your idea is good. Could also be an object that would hold a state in case we have many plugins implementing the hook, so it creates the explanation in the first call and stores it to be returned in any following call. And that does not format the explanation at all if no one uses it. I will try to come up with something when I have time. However the thing I really cannot seem to do is to understand this leak and cyclic reference failures. Any idea how to approach this? |
Let's see if using the lazy approach gets rid of the leaks, as I suspect they might. |
c03f60e
to
e37c58c
Compare
I have investigated a bit more and I think I can pinpoint the exact statement causing the leak: It seems that problem is having Does it make sense that calling locals on the test causes a circular reference or something? |
One more interesting case: I changed
This test passes. However, if I just uncomment the |
Some update: It seems then that calling Therefore the tests seem to pass with the test like this:
I will now work on making the explanation creation lazy. |
One answer seems to confirm that |
Current errors on CI are mainly:
Guys, about avoiding unnecessary explanation calculation, I think that the best way is to check if any plugins implement the hookspec. If none does, then we only explain for failures (as before). I think it is going to make everything simpler than passing a lazy object, which would also make the API more weird to use. What do you think @nicoddemus and @RonnyPfannschmidt? |
I added this to Pluggy (pytest-dev/pluggy#177) and updated the code in this PR to use it. It seems to work just as expected. However I was checking this saving the unparsed ast manually with/without hooks and comparing the modified source. I have no idea how to test it to make it part of the unit tests. Ideas? |
Oh well, now I just realized that having the AST code dependent also on the hook implementation by the plugins will create some problems for caching (now not only source file dependent anymore), just like discussed in #3479 |
Yeah... no ideas come to mind at the moment to handle that. 😕 |
Codecov Report
@@ Coverage Diff @@
## master #3479 +/- ##
==========================================
- Coverage 96.08% 96.07% -0.01%
==========================================
Files 117 117
Lines 25564 25642 +78
Branches 2475 2484 +9
==========================================
+ Hits 24562 24636 +74
- Misses 698 700 +2
- Partials 304 306 +2
Continue to review full report at Codecov.
|
Thanks for bringing this to attention again! Most of the concerns here are regarding performance. How about if this rewriting is opt-in, controlled by an If plugins need this enabled, they can initially document so, but potentially in the future we can provide a way to enable this programmatically in a way that those plugins can do it at runtime. Finally, we should mark the entire feature as experimental so we can revert it in case we discover some serious problem with it once it is in the wild. |
What I am doing now is to check whether any plugins implement the
The pytest/src/_pytest/assertion/__init__.py Lines 143 to 146 in 1cbafeb
Would that be a reasonable solution? |
Oh that is a good approach. I would still mark the hook as experimental thought. @Sup3rGeo do you need this support in Python 2? I ask because we are about to release 4.6, which is supposed to be the last version supporting Python 2. |
Probably not! |
As per #5373 (comment), please rebase this on |
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 think we are almost there, thanks for bearing with me so far!
As I stated, this is a very tricky part of the code so I'm trying to be as comprehensive as possible on my review. 🙇
Co-Authored-By: Bruno Oliveira <nicoddemus@gmail.com>
I've added this to the 5.0 milestone, I'm confident it is complete enough to go into the 5.0 release. 🎉 @Sup3rGeo let me know if you won't have time to finish this off in the next day or two, I will try to pick it up then. 👍 |
…mplementing the hook.
…n-pass-hook-master # Conflicts: # src/_pytest/assertion/rewrite.py
…tion_pass_impl function call.
I guess by this point it should be mostly done. |
Thanks @Sup3rGeo! I noticed the default value for the option was I also took sometime to refine the docs and CHANGELOG a bit. 👍 As soon as CI passes we can merge this I believe. Thanks a lot again, for your perseverance and patience under my barrage of requests! 😁 |
Small optimization, move the generation of the intermediate formatting variables inside the 'if _check_if_assertion_pass_impl():' block.
Pushed another small change: I moved the creation of the formatting variables when the assertion passes under the
This is a small optimization which is important if/when we eventually drop the requirement of using the |
🎉 |
Thanks @nicoddemus, I see there were a lot of tweaks! |
Implements #3457.
Already has documentation and one simple test. Pending questions:
Three pytest tests are not passing:
FAIL testing/acceptance_test.py::test_fixture_values_leak
FAIL testing/python/raises.py::TestRaises::()::test_raises_cyclic_reference[function]
FAIL testing/python/raises.py::TestRaises::()::test_raises_cyclic_reference[with]
Need help to understand why they are failing.
Always generating explanations (which could be expensive): As @nicoddemus mentionned this should be avoided BUT if assertion fails, we will generate them to report the failure. If the assert passes, users of the hook need it anyways, so it needs to be generated.
Is this test enough?
Thanks!