From ab003dbd92bf7980157da84ccb4b910d06081db6 Mon Sep 17 00:00:00 2001 From: Zsailer Date: Thu, 1 Aug 2019 16:10:35 -0400 Subject: [PATCH 1/4] allow server standalone mode --- jupyter_server/extension/application.py | 9 +++------ jupyter_server/extension/handler.py | 4 ++++ jupyter_server/serverapp.py | 14 +++++++++++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/jupyter_server/extension/application.py b/jupyter_server/extension/application.py index 791bc9fca6..db9909c609 100644 --- a/jupyter_server/extension/application.py +++ b/jupyter_server/extension/application.py @@ -152,8 +152,8 @@ def _prepare_config(self): the object to the webapp's settings as `_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 @@ -279,7 +279,6 @@ def load_jupyter_server_extension(cls, serverapp, argv=[], **kwargs): # Configure and initialize extension. extension = cls() extension.initialize(serverapp, argv=argv) - return extension @classmethod @@ -290,7 +289,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: @@ -313,11 +311,10 @@ 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) diff --git a/jupyter_server/extension/handler.py b/jupyter_server/extension/handler.py index ea6f8ba5d6..22737ed3bb 100644 --- a/jupyter_server/extension/handler.py +++ b/jupyter_server/extension/handler.py @@ -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( diff --git a/jupyter_server/serverapp.py b/jupyter_server/serverapp.py index 801ec7b22c..2f6826dc78 100755 --- a/jupyter_server/serverapp.py +++ b/jupyter_server/serverapp.py @@ -492,6 +492,11 @@ def start(self): _("Allow the server to be run from root user.") ) +flags['standalone']=( + {'ServerApp' : {'standalone' : True}}, + _("Run the server without enabling extensions.") +) + # Add notebook manager flags flags.update(boolean_flag('script', 'FileContentsManager.save_script', 'DEPRECATED, IGNORED', @@ -1138,6 +1143,12 @@ def _update_server_extensions(self, change): is not available. """)) + standalone = Bool( + False, + config=True, + help="Run the server without enabling extensions." + ) + def parse_command_line(self, argv=None): super(ServerApp, self).parse_command_line(argv) @@ -1469,7 +1480,8 @@ def initialize(self, argv=None): self.init_webapp() self.init_terminals() self.init_signal() - self.init_server_extensions() + if self.standalone is False: + self.init_server_extensions() self.init_mime_overrides() self.init_shutdown_no_activity() From 8867d42dadab3f1a2d26173da890913792c5ef2d Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Thu, 15 Aug 2019 13:22:34 +0100 Subject: [PATCH 2/4] Add StandaloneApp as a separate class --- jupyter_server/extension/application.py | 54 +++++++++++++++++-------- jupyter_server/serverapp.py | 15 +------ 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/jupyter_server/extension/application.py b/jupyter_server/extension/application.py index db9909c609..7a97b87ba9 100644 --- a/jupyter_server/extension/application.py +++ b/jupyter_server/extension/application.py @@ -61,17 +61,17 @@ def _preparse_command_line(Application): app.exit(0) -class ExtensionApp(JupyterApp): + +class StandaloneApp(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 """ + + standalone = True + # Name of the extension extension_name = Unicode( "", @@ -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))) @@ -219,15 +219,15 @@ 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) + serverapp.initialize(argv=argv, load_extensions=cls.standalone is False) return serverapp def initialize(self, serverapp, argv=[]): @@ -242,7 +242,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. @@ -272,8 +272,8 @@ 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. @@ -321,3 +321,25 @@ def launch_instance(cls, argv=None, **kwargs): # Start the ioloop. extension.start_server() + +class ExtensionApp(StandaloneApp): + """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 + """ + + standalone = False + + @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) diff --git a/jupyter_server/serverapp.py b/jupyter_server/serverapp.py index 2f6826dc78..ea944040c5 100755 --- a/jupyter_server/serverapp.py +++ b/jupyter_server/serverapp.py @@ -492,11 +492,6 @@ def start(self): _("Allow the server to be run from root user.") ) -flags['standalone']=( - {'ServerApp' : {'standalone' : True}}, - _("Run the server without enabling extensions.") -) - # Add notebook manager flags flags.update(boolean_flag('script', 'FileContentsManager.save_script', 'DEPRECATED, IGNORED', @@ -1143,12 +1138,6 @@ def _update_server_extensions(self, change): is not available. """)) - standalone = Bool( - False, - config=True, - help="Run the server without enabling extensions." - ) - def parse_command_line(self, argv=None): super(ServerApp, self).parse_command_line(argv) @@ -1470,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: @@ -1480,7 +1469,7 @@ def initialize(self, argv=None): self.init_webapp() self.init_terminals() self.init_signal() - if self.standalone is False: + if load_extensions is True: self.init_server_extensions() self.init_mime_overrides() self.init_shutdown_no_activity() From e8053f80c21531c45764650fe969176314cc8dae Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Thu, 15 Aug 2019 17:00:41 +0100 Subject: [PATCH 3/4] Make StandalonApp and ExtensionApp siblings --- jupyter_server/extension/application.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/jupyter_server/extension/application.py b/jupyter_server/extension/application.py index 7a97b87ba9..75e3e2c305 100644 --- a/jupyter_server/extension/application.py +++ b/jupyter_server/extension/application.py @@ -62,7 +62,7 @@ def _preparse_command_line(Application): -class StandaloneApp(JupyterApp): +class ExtensionAppBase(JupyterApp): """Base class for configurable Jupyter Server Extension Applications. StandaloneApp subclasses are launched directly by calling its @@ -70,7 +70,7 @@ class StandaloneApp(JupyterApp): entry_point in the extensions setup.py """ - standalone = True + standalone = None # Name of the extension extension_name = Unicode( @@ -227,7 +227,12 @@ def initialize_server(cls, argv=[], **kwargs): # Initialize ServerApp config. # Parses the command line looking for # ServerApp configuration. - serverapp.initialize(argv=argv, load_extensions=cls.standalone is False) + if cls.standalone not in (True, False): + raise ValueError( + 'Application "standalone" class attribute needs to be set ' + 'to either True or False') + serverapp.initialize( + argv=argv, load_extensions=cls.standalone is False) return serverapp def initialize(self, serverapp, argv=[]): @@ -322,7 +327,11 @@ def launch_instance(cls, argv=None, **kwargs): extension.start_server() -class ExtensionApp(StandaloneApp): +class StandaloneApp(ExtensionAppBase): + standalone = True + + +class ExtensionApp(ExtensionAppBase): """Base class for configurable Jupyter Server Extension Applications. ExtensionApp subclasses can be initialized two ways: From 4c4c2e7a2b367fdaa7d73ab67587198b8149ba71 Mon Sep 17 00:00:00 2001 From: Vidar Tonaas Fauske Date: Thu, 15 Aug 2019 17:19:40 +0100 Subject: [PATCH 4/4] Make things more explicit --- jupyter_server/extension/application.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/jupyter_server/extension/application.py b/jupyter_server/extension/application.py index 75e3e2c305..41678d725b 100644 --- a/jupyter_server/extension/application.py +++ b/jupyter_server/extension/application.py @@ -70,7 +70,7 @@ class ExtensionAppBase(JupyterApp): entry_point in the extensions setup.py """ - standalone = None + load_other_extensions = True # Name of the extension extension_name = Unicode( @@ -227,12 +227,12 @@ def initialize_server(cls, argv=[], **kwargs): # Initialize ServerApp config. # Parses the command line looking for # ServerApp configuration. - if cls.standalone not in (True, False): + if cls.load_other_extensions not in (True, False): raise ValueError( - 'Application "standalone" class attribute needs to be set ' + '"load_other_extensions" class attribute needs to be set ' 'to either True or False') serverapp.initialize( - argv=argv, load_extensions=cls.standalone is False) + argv=argv, load_extensions=cls.load_other_extensions) return serverapp def initialize(self, serverapp, argv=[]): @@ -328,7 +328,9 @@ def launch_instance(cls, argv=None, **kwargs): class StandaloneApp(ExtensionAppBase): - standalone = True + # 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): @@ -343,8 +345,6 @@ class method. This method can be set as a entry_point in the extensions setup.py """ - standalone = False - @classmethod def load_jupyter_server_extension(cls, serverapp, argv=[], **kwargs): """Initialize and configure this extension, then add the extension's