-
Notifications
You must be signed in to change notification settings - Fork 310
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
Standalone mode set by extension authors #76
Conversation
I thought that adding - def initialize(self, argv=None):
+ def initialize(self, argv=None, load_extensions=True):
super(ServerApp, self).initialize(argv)
self.init_logging()
...
self.init_signal()
- self.init_server_extensions()
+ if load_extensions is True:
+ self.init_server_extensions() |
That is pretty elegant 😃 Is there any danger to changing |
I guess I still prefer an empty subclass class StandaloneApp(ExtensionApp):
pass then use... if not isinstance(self, StandaloneApp):
self.init_server_extensions() Then no |
In the case of two serverapp extensions (
|
Thanks @SylvainCorlay - that's interesting and great example. It seems like in the latter case, voila is merely a server extension and not an application and it would be nice that that be expressed in a class hierarchy somehow. Some StandaloneApps are also extensions and some are not. All ExtensionApps are also extensions by default. Is there a way to express that? I apologize for dragging this on. Perhaps this is a matter of clarifying the We could then remove Can an "app" distinguish between it being primary vs. secondary - both at load and runtime? I think that would be useful. |
It does not seem to me that there is really a distinction. I was merely assuming the |
Given that we could possibly want all 4 combinations of "autoload me/not" × "autoload others/not", I believe the server app having a flag "load other extensions/not" should be sufficient (#76). Apps that do not want to be used as extensions should then not expose the server extension "magics" in the module. |
No, I was confused about where I was posting, not where I was linking 😀 |
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.
If we can pass flag instead of writing private var (without breaking anything), that would be better, but I think this concept is currently the best one.
@@ -1469,7 +1469,8 @@ def initialize(self, argv=None): | |||
self.init_webapp() | |||
self.init_terminals() | |||
self.init_signal() | |||
self.init_server_extensions() | |||
if load_extensions: | |||
self.init_server_extensions() |
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 it might be a good idea to log an informational message when load_extensions
is False stating that "<extension_name> is running in exclusive mode" (or whatever terminology is appropriate). This way any confusion as to why other extensions aren't appearing is more clear.
Likewise, I think a debug message in init_server_extensions()
stating which extension is being loaded would also be helpful information. This way admins can confirm their configurations are behaving as expected.
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.
Added some logging messages.
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.
Another comment for now. (Not sure if duplicates were created - on a bouncy bus at the moment - I apologize if that was the case.)
jupyter_server/serverapp.py
Outdated
@@ -1415,6 +1415,8 @@ def init_server_extensions(self): | |||
func = getattr(mod, 'load_jupyter_server_extension', None) | |||
if func is not None: | |||
func(self) | |||
# Add debug log for loaded extensions. | |||
self.log.debug("%s is enabled and loaded.", modulename)) |
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.
These should be indented.
We should probably add an else:
clause that logs a warning stating that the extension, while enabled, doesn't have a load_jupyter_server_extension()
method.
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.
Good catch!
👍 Warning added.
load_other_extensions=cls.load_other_extensions | ||
) | ||
# Log if extension is blocking other extensions from loading. | ||
if cls.load_other_extensions: |
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.
@kevin-bates Here's the other logging message.
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.
Thanks Zach. I'm wondering if the phrase "is running without loading other extensions." could get construed as a problematic symptom. That is, admins may not necessarily know that a given "primary" extension is configured for exclusive use and might wonder - why is it not loading other extensions? Just thinking that a phrase like the following might be more clear:
"{ext_name} is configured for exclusive use - no other extensions will be loaded."
This way, the intention is clear and not as ambiguous.
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.
@Zsailer - I just noticed that the polarity on the condition is not correct. This should only occur if load_other_extensions
is False.
Also, I'm thinking it might be better to move this log statement into ServerApp.initialize()
as an else
condition to where the other extensions are being loaded - or is that at too low a level?
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.
Oh my. Good catch and sorry for the sloppiness! 🤦♂
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.
Also, I'm thinking it might be better to move this log statement into ServerApp.initialize() as an else condition to where the other extensions are being loaded - or is that at too low a level?
My hesitation here is that we'd need to add another argument, extension_name
, to ServerApp.initialize()
. Right now, the ServerApp is completely unaware of ExtensionApps. Adding this argument would break this pattern. Do we think that's ok?
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.
Ah - that makes sense - good point. Let's keep that pattern in tact as long as possible.
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.
"{ext_name} is configured for exclusive use - no other extensions will be loaded."
I think the word "configured" might be confusing since it's not configurable by the user.
How about:
"jupyter {ext_name}" runs without loading other extensions. If you would like to run {ext_name} with other extensions, try runnig "jupyter server" with {ext_name} enabled.
I'm in favor of flags over inheritance. This allows for extensions that can act standalone as well as cooperative. There's nothing wrong with having a convenience class that sets the flag by default. But the behavior should be driven by the flag, not by the class hierarchy. |
@rolweber - good point. The dual-mode behavior of Voila that @Sylvain stated put a damper on a class-based approach since StandaloneApps could then also be cooperative - which makes sense. I think we've boiled this down to two primary ways to start the server, each with a couple semantic differences: Here are some concrete examples that should illustrate the differences ... (I've also used "footnotes" for questions.)
[1]: By "server extension" are we (now) always talking about applications that extend [2]: Is there a way for an ExtensionApp to say that it doesn't want to be 'cooperative'? I.e., extension authors require "complete exclusivity". Is allowing such a thing necessary? [3]: Can a given [4]: What should the behavior be if no extension apps are configured and enabled? Currently, it runs but I'm not sure how useful that is. However, I suppose this could be a way to validate a minimum configuration in times of troubleshooting, adding server extensions at each step - so I guess I'd be inclined to leave this alone. |
One clear step would be to not define the server extension interfaces in the package (server extension path function, server extension load function), so that it cannot be used as an extension. Another clear step would be to name all the app classes with a leading underscore to hint that "this class is not meant for reuse". I don't think we should do anything from this package's side. |
It was my understanding that by server extension we don't only talk about ExtensionApps. Basically we start creating server extensions as soon as we need new tornado entry points. ExtensionApps are the subset of server extensions that has a default URL that we may want to start with a python entry point.
Good question. I don't see a usecase for that at the moment though. The reason why I brought up the
It may be useful to e.g. disallow execution requests in the case of voilà, although since we should have the previously described alias, it is probably fine as-is.
I presume that the default start URL should be some jupyter equivalent to Apache's "it works" and some documentation about how to install extension apps. When extension apps are enabled, they could be listed on that page. |
Thank you @SylvainCorlay - that's helpful. |
Any reason not to merge this? |
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
My alternative solution to #70 and #75.
This makes standalone mode an option only set by extension authors, not a configurable trait that users can set...