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

Fix setting connector to re-enable saving user settings (cf. #7147) #7601

Merged
merged 2 commits into from Dec 11, 2019

Conversation

telamonian
Copy link
Member

@telamonian telamonian commented Dec 7, 2019

References

followup #7147

In the latest master, you cannot change/save any of the user settings. This PR fixes that.

Code changes

Digging through the merged PRs, it looks like in #7147 a save method was left out of the SettingConnector that now wraps the preexisting SettingManager. I added SettingConnector.save, which just forwards SettingManager.save.

I also did a little bit of related code cleanup in apputils-extension, and moved the settings plugin out into its own src file (mostly for the sake of making it easier for me to grasp what had broken)

User-facing changes

Unbreaks settings

Backwards-incompatible changes

None

@jupyterlab-dev-mode
Copy link

Thanks for making a pull request to JupyterLab!

To try out this branch on binder, follow this link: Binder

@telamonian telamonian changed the title Implement save settingconnector followup #7147: adds SettingConnector.save, reenables saving of user settings Dec 7, 2019
@telamonian telamonian added this to the 2.0 milestone Dec 7, 2019
@telamonian
Copy link
Member Author

@afshin I was a little lost at first going through the changes in #7174, but your earlier discussion with @vidartf made things a lot clearer:

I thought about putting it in services, but it seemed very specific to the functionality of the SettingRegistry and it seemed to me that the services package should be completely agnostic about what you're doing with the data being returned from the back-end.

One other question I had, though: why does SettingConnector wrap SettingManager instead of, say, subclassing it (since both are themselves subclasses of DataConnector<ISettingRegistry.IPlugin, string>)? Is there any particular reason to prefer one over the other?

@afshin
Copy link
Member

afshin commented Dec 8, 2019

why does SettingConnector wrap SettingManager instead of, say, subclassing it (since both are themselves subclasses of DataConnector<ISettingRegistry.IPlugin, string>)? Is there any particular reason to prefer one over the other?

The reasoning was that I didn't want to need to know the SettingManager instantiation options and worry if they'd ever change in the future and instead wanted to rely on the already extant version in the services package.

@@ -40,5 +40,9 @@ export class SettingConnector extends DataConnector<
};
}

async save(id: string, raw: string): Promise<void> {
Copy link
Member

Choose a reason for hiding this comment

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

🍳 🥚 on my face. Thanks for the fix 👍

@@ -40,5 +40,9 @@ export class SettingConnector extends DataConnector<
};
}

async save(id: string, raw: string): Promise<void> {
await this._connector.save(id, raw);
Copy link
Member

Choose a reason for hiding this comment

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

I have a slight preference toward return this._connector.save(id, raw); but not a strong one. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

I was going to say "I'm happy to change it just because you asked, @afshin", but then I looked and saw that the function it's wrapping is also async:

async save(id: string, raw: string): Promise<void> {

so for consistency's sake, I think save should stick with async/await here.

To be 100% sure, the async and return Promise versions are semantically equivalent: both will immediately return a Promise that resolves upon completion of the wrapped function, right?

Copy link
Member

Choose a reason for hiding this comment

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

Is there an extra promise getting wrapped up in here? Basically I'm wondering if these three are equivalent or if they return a different level of nesting.

save(id: string, raw: string): Promise<void> {
  return this._connector.save(id, raw);
}
async save(id: string, raw: string): Promise<void> {
  return this._connector.save(id, raw);
}
async save(id: string, raw: string): Promise<void> {
  await this._connector.save(id, raw);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I've read over the async/await docs and I've live inspected all three versions in DevTools. Here's my conclusions:

  • From the perspective of a caller, 1, 2, and 3 are identical. All return a singly nested promise that resolves to an undefined.

  • 1 and 2 are actually identical. If a function is already returning a Promise, all adding the async keyword does is allow for the use of await within the function body.

  • 3 might involve one extra step in it's evaluation. Basically, 1/2 just return the inner promise, whereas 3 returns a promise that resolves when the wrapper function finishes. The extra step would then be the implicit return void 0 that the wrapper function calls after it awaits the inner promise.

So at least I can confidently say that no, there's no extra promise getting wrapped up here. I can also say that for our purposes there are no important distinctions between 1/2/3. @afshin Do you still think I should change it?

Copy link
Member

@afshin afshin Dec 10, 2019

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

(This is all very pedantic, but it's good to know what JS we are actually generating.)

Copy link
Member

@afshin afshin left a comment

Choose a reason for hiding this comment

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

👍

@telamonian
Copy link
Member Author

I don't want it to delay getting a fix in for the settings bug, but it would be neat to get someone familiar with the deep magics of the JS runtime to weigh in as to whether there's any difference whatsoever in practice between the different ways to wrap an async function like save. In Python, you can always just inspect the bytecode, but I don't know what the equivalent is for JS.

@afshin afshin merged commit ea87dd7 into jupyterlab:master Dec 11, 2019
@afshin afshin changed the title followup #7147: adds SettingConnector.save, reenables saving of user settings Fix SettingConnector.save to re-enable saving user settings (cf. #7147) Dec 12, 2019
@afshin afshin changed the title Fix SettingConnector.save to re-enable saving user settings (cf. #7147) Fix setting connector to re-enable saving user settings (cf. #7147) Dec 12, 2019
@lock lock bot added the status:resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion. label Jan 11, 2020
@lock lock bot locked as resolved and limited conversation to collaborators Jan 11, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug pkg:apputils status:resolved-locked Closed issues are locked after 30 days inactivity. Please open a new issue for related discussion. tag:Settings
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants