-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Interactivity: Restore scope when yielded promise rejects #59708
Conversation
Size Change: +5 B (0%) Total Size: 1.71 MB
ℹ️ View Unchanged
|
8b49642
to
5d08081
Compare
setNamespace( ns ); | ||
setScope( scope ); | ||
gen.throw( e ); | ||
} finally { | ||
resetScope(); | ||
resetNamespace(); |
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.
@DAreRodz I tried 2 different approaches and they both fixed my test case. I wasn't sure why we have 2 try blocks, so my first version merge the try/finally and try/catch into one try/catch/finally. Then, since I wasn't sure what the motivation for 2 blocks were, I changed use 2 blocks. I'd like to get your insight here.
Both versions are on this branch in different commits and can be reviewed.
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 this is the best approach: to have two separate try-catch blocks. The thing with async actions is that they can change the scope at any time―whenever they run―, thus leading to unexpected results when the scope is reset.
We decided here to set/reset the scope for synchronous code fragments, i.e., the generator code from one yield
to the next one. So, from my understanding, we should reset the scope, then wait for the promise to be fulfilled (which will probably set their own scope), and if it fails, then set/reset the scope again.
packages/e2e-tests/plugins/interactive-blocks/generator-scope/render.php
Outdated
Show resolved
Hide resolved
@@ -77,6 +77,7 @@ | |||
<!-- Exclude PHPUnit tests from file and class name sniffs (for Core parity). --> | |||
<rule ref="WordPress.Files.FileName.NotHyphenatedLowercase"> | |||
<exclude-pattern>/phpunit/*</exclude-pattern> | |||
<exclude-pattern>*\.asset\.php$</exclude-pattern> |
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.
See #59705 (comment)
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
@sirreal, nice job finding and proposing the fix for the bug. Unrelated to this PR. I'm a little bit afraid, that the current testing strategy doesn’t scale well. To run a single test, you need to write a new block in the first place. To run the test, you need to have a WordPress instance running, activate the plugin, add a post with the block, and then perform all the cleanup after the test. It all takes time and resources on CI, and isn't easy to run for contributors locally. What was the rationale for avoiding using the unit tests for the internal runtime implementation? |
I think that's a good point. I followed the existing testing pattern, but I'll look into adding some unit tests as well. Edit: I think it would be worthwhile to add some unit tests, but I'd like to land this PR without them. Implementing a unit test for this would require me to get much more familiar with the framework. I think it's worth it to get this fix into a release, ideally it can ship with WordPress 6.5. |
That would be a big ask to define a unit testing strategy in a bug fix in one of the first contributions to the package 🙈 The code is running on millions of websites sites using interactive core blocks covered by existing e2e tests, so that can't be a blocker for this PR 😄 |
Since we're past RC1, only severe bug fixes are allowed into 6.5 at this point, so my question is how important it is for the current PR to land in 6.5. |
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.
LGTM!
Nitpick: maybe we can rename the test file to something more general, as these are e2e tests. Something like async-actions.spec.ts
or similar.
@youknowriad, I think this fix would be similar to this bugfix. It's not like this bug breaks any block or site, but it breaks developers' expectations while using the Interactivity API. In that regard, I would say this bugfix "ensures that the contract provided by the Interactivity API is maintained". Asynchronous actions should keep the scope during their whole execution―even when exceptions arise―and the example shared by @sirreal in the description is a valid and expected use case. |
I agree with @DAreRodz, this is a fix that seems quite safe for a bug that I discovered very quickly when playing with the Interactivity API loosely based on some code samples in the Interactivity API dev note. I think it would be a shame to release 6.5 and the Interactivity API knowing that it has this bug. The bug is clear and the fix seems low risk. |
Co-authored-by: Anton Vlasenko <[email protected]>
I'm making this change and I'll add an entry to the package changelog, then I'll merge.
|
dc5fc36
to
242f138
Compare
…59708) Ensure scope is properly restored when an exception is thrown in a generator callback. For example, the following block should have the correct context in the catch block: try { yield Promise.reject( '💥' ); } catch ( err ) { getContext().error = err; } --- Co-authored-by: sirreal <[email protected]> Co-authored-by: DAreRodz <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: youknowriad <[email protected]> Co-authored-by: anton-vlasenko <[email protected]> # Conflicts: # packages/interactivity/CHANGELOG.md
I just manually cherry-picked this PR to the pick/wp-65-rc-2 branch to get it included in the next release |
…59708) Ensure scope is properly restored when an exception is thrown in a generator callback. For example, the following block should have the correct context in the catch block: try { yield Promise.reject( '💥' ); } catch ( err ) { getContext().error = err; } --- Co-authored-by: sirreal <[email protected]> Co-authored-by: DAreRodz <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: youknowriad <[email protected]> Co-authored-by: anton-vlasenko <[email protected]> # Conflicts: # packages/interactivity/CHANGELOG.md
…ordPress#59708) Ensure scope is properly restored when an exception is thrown in a generator callback. For example, the following block should have the correct context in the catch block: try { yield Promise.reject( '💥' ); } catch ( err ) { getContext().error = err; } --- Co-authored-by: sirreal <[email protected]> Co-authored-by: DAreRodz <[email protected]> Co-authored-by: gziolo <[email protected]> Co-authored-by: youknowriad <[email protected]> Co-authored-by: anton-vlasenko <[email protected]>
What?
Ensure that the interactivity scope is maintained when a promise rejects.
Why?
I expected to be able to do something like this in an interactivity callback:
But the
getContext()
in the catch block was alwaysundefined
.How?
Ensure that scope is properly set when handling rejection.
Testing Instructions
This includes an e2e test.