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

Add StandaloneApp as a separate class #75

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 50 additions & 22 deletions jupyter_server/extension/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,17 @@ def _preparse_command_line(Application):
app.exit(0)


class ExtensionApp(JupyterApp):

class ExtensionAppBase(JupyterApp):
"""Base class for configurable Jupyter Server Extension Applications.

ExtensionApp subclasses can be initialized two ways:
1. Extension is listed as a jpserver_extension, and ServerApp calls
its load_jupyter_server_extension classmethod. This is the
classic way of loading a server extension.
2. Extension is launched directly by calling its `launch_instance`
class method. This method can be set as a entry_point in
the extensions setup.py
StandaloneApp subclasses are launched directly by calling its
`launch_instance` class method. This method can be set as an
entry_point in the extensions setup.py
"""

load_other_extensions = True

# Name of the extension
extension_name = Unicode(
"",
Expand All @@ -88,11 +88,11 @@ def _validate_extension_name(self):
value = self.extension_name
if isinstance(value, str):
# Validate that extension_name doesn't contain any invalid characters.
for c in ExtensionApp.INVALID_EXTENSION_NAME_CHARS:
for c in StandaloneApp.INVALID_EXTENSION_NAME_CHARS:
if c in value:
raise ValueError("Extension name '{name}' cannot contain any of the following characters: "
"{invalid_chars}.".
format(name=value, invalid_chars=ExtensionApp.INVALID_EXTENSION_NAME_CHARS))
format(name=value, invalid_chars=StandaloneApp.INVALID_EXTENSION_NAME_CHARS))
return value
raise ValueError("Extension name must be a string, found {type}.".format(type=type(value)))

Expand Down Expand Up @@ -152,8 +152,8 @@ def _prepare_config(self):
the object to the webapp's settings as `<extension_name>_config`.
"""
traits = self.class_own_traits().keys()
self.config = Config({t: getattr(self, t) for t in traits})
self.settings['{}_config'.format(self.extension_name)] = self.config
self.extension_config = Config({t: getattr(self, t) for t in traits})
self.settings['{}_config'.format(self.extension_name)] = self.extension_config

def _prepare_settings(self):
# Make webapp settings accessible to initialize_settings method
Expand Down Expand Up @@ -219,15 +219,20 @@ def _prepare_templates(self):
})
self.initialize_templates()

@staticmethod
def initialize_server(argv=[], **kwargs):
@classmethod
def initialize_server(cls, argv=[], **kwargs):
"""Get an instance of the Jupyter Server."""
# Get a jupyter server instance
serverapp = ServerApp(**kwargs)
# Initialize ServerApp config.
# Parses the command line looking for
# ServerApp configuration.
serverapp.initialize(argv=argv)
if cls.load_other_extensions not in (True, False):
raise ValueError(
'"load_other_extensions" class attribute needs to be set '
'to either True or False')
serverapp.initialize(
argv=argv, load_extensions=cls.load_other_extensions)
return serverapp

def initialize(self, serverapp, argv=[]):
Expand All @@ -242,7 +247,7 @@ def initialize(self, serverapp, argv=[]):
"""
self._validate_extension_name()
# Initialize the extension application
super(ExtensionApp, self).initialize(argv=argv)
super(StandaloneApp, self).initialize(argv=argv)
self.serverapp = serverapp

# Initialize config, settings, templates, and handlers.
Expand Down Expand Up @@ -272,14 +277,13 @@ def start_server(self, **kwargs):
self.serverapp.start(**kwargs)

@classmethod
def load_jupyter_server_extension(cls, serverapp, argv=[], **kwargs):
"""Initialize and configure this extension, then add the extension's
def _load_app(cls, serverapp, argv=[], **kwargs):
"""Initialize and configure this app, then add the apps's
settings and handlers to the server's web application.
"""
# Configure and initialize extension.
extension = cls()
extension.initialize(serverapp, argv=argv)

return extension

@classmethod
Expand All @@ -290,7 +294,6 @@ def _prepare_launch(cls, serverapp, argv=[], **kwargs):
"""
# Load the extension
extension = cls.load_jupyter_server_extension(serverapp, argv=argv, **kwargs)

# Start the browser at this extensions default_url, unless user
# configures ServerApp.default_url on command line.
try:
Expand All @@ -313,14 +316,39 @@ def launch_instance(cls, argv=None, **kwargs):
# arguments trigger actions from the extension not the server.
_preparse_command_line(cls)
# Handle arguments.
if argv is not None:
if argv is None:
args = sys.argv[1:] # slice out extension config.
else:
args = []

# Get a jupyter server instance.
serverapp = cls.initialize_server(argv=args)
extension = cls._prepare_launch(serverapp, argv=args, **kwargs)
# Start the ioloop.
extension.start_server()


class StandaloneApp(ExtensionAppBase):
# This is most often the case, but it can make sense for a subclass to
# want to load other enabled extensions
load_other_extensions = False


class ExtensionApp(ExtensionAppBase):
"""Base class for configurable Jupyter Server Extension Applications.

ExtensionApp subclasses can be initialized two ways:
1. Extension is listed as a jpserver_extension, and ServerApp calls
its load_jupyter_server_extension classmethod. This is the
classic way of loading a server extension.
2. Extension is launched directly by calling its `launch_instance`
class method. This method can be set as a entry_point in
the extensions setup.py
"""

@classmethod
def load_jupyter_server_extension(cls, serverapp, argv=[], **kwargs):
"""Initialize and configure this extension, then add the extension's
settings and handlers to the server's web application.
"""
# Configure and initialize extension.
return cls._load_app(serverapp, argv=argv, **kwargs)
4 changes: 4 additions & 0 deletions jupyter_server/extension/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def initialize(self, extension_name):
def config(self):
return self.settings["{}_config".format(self.extension_name)]

@property
def server_config(self):
return self.settings["config"]

@property
def static_url_prefix(self):
return "/static/{extension_name}/".format(
Expand Down
5 changes: 3 additions & 2 deletions jupyter_server/serverapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1459,7 +1459,7 @@ def init_shutdown_no_activity(self):
pc.start()

@catch_config_error
def initialize(self, argv=None):
def initialize(self, argv=None, load_extensions=True):
super(ServerApp, self).initialize(argv)
self.init_logging()
if self._dispatching:
Expand All @@ -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 is True:
self.init_server_extensions()
self.init_mime_overrides()
self.init_shutdown_no_activity()

Expand Down