Skip to content

Commit

Permalink
stop hook for extensions
Browse files Browse the repository at this point in the history
* closes #241
* call a stop_extension method on server shutdown if present
  • Loading branch information
oliver-sanders committed May 21, 2021
1 parent 5f76fda commit 7eb714e
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 3 deletions.
5 changes: 5 additions & 0 deletions docs/source/developers/extensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ The basic structure of an ExtensionApp is shown below:
...
# Change the jinja templating environment
def stop_extension(self):
...
# Perform any required shut down steps
The ``ExtensionApp`` uses the following methods and properties to connect your extension to the Jupyter server. You do not need to define a ``_load_jupyter_server_extension`` function for these apps. Instead, overwrite the pieces below to add your custom settings, handlers and templates:

Expand All @@ -164,6 +168,7 @@ Methods
* ``initialize_setting()``: adds custom settings to the Tornado Web Application.
* ``initialize_handlers()``: appends handlers to the Tornado Web Application.
* ``initialize_templates()``: initialize the templating engine (e.g. jinja2) for your frontend.
* ``stop_extension()``: called on server shut down.

Properties

Expand Down
2 changes: 2 additions & 0 deletions jupyter_server/extension/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ def start(self):
def stop(self):
"""Stop the underlying Jupyter server.
"""
if hasattr(self, 'stop_extension'):
self.stop_extension()
self.serverapp.stop()
self.serverapp.clear_instance()

Expand Down
29 changes: 26 additions & 3 deletions jupyter_server/extension/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,15 +230,17 @@ def link_point(self, point_name, serverapp):

def load_point(self, point_name, serverapp):
point = self.extension_points[point_name]
point.load(serverapp)
return point.load(serverapp)

def link_all_points(self, serverapp):
for point_name in self.extension_points:
self.link_point(point_name, serverapp)

def load_all_points(self, serverapp):
for point_name in self.extension_points:
return [
self.load_point(point_name, serverapp)
for point_name in self.extension_points
]


class ExtensionManager(LoggingConfigurable):
Expand Down Expand Up @@ -285,6 +287,13 @@ def _config_manager_changed(self, change):
"""
)

extension_apps = Dict(
help="""
Dictionary with extension names as keys
and ExtensionApp objects as values.
"""
)

@property
def extension_points(self):
extensions = self.extensions
Expand Down Expand Up @@ -338,12 +347,20 @@ def load_extension(self, name, serverapp):
extension = self.extensions.get(name)
if extension.enabled:
try:
extension.load_all_points(serverapp)
self.extension_apps.setdefault(name, []).extend(
extension.load_all_points(serverapp)
)
self.log.info("{name} | extension was successfully loaded.".format(name=name))
except Exception as e:
self.log.debug("".join(traceback.format_exception(*sys.exc_info())))
self.log.warning("{name} | extension failed loading with message: {error}".format(name=name,error=str(e)))

def stop_extension(self, name, apps):
"""Call the shutdown hooks in the specified apps."""
for app in apps:
if hasattr(app, 'stop_extension'):
app.stop_extension()

def link_all_extensions(self, serverapp):
"""Link all enabled extensions
to an instance of ServerApp
Expand All @@ -361,3 +378,9 @@ def load_all_extensions(self, serverapp):
# order.
for name in sorted(self.extensions.keys()):
self.load_extension(name, serverapp)

def stop_all_extensions(self, serverapp):
"""Call the shutdown hooks in all extensions."""
for name, apps in sorted(dict(self.extension_apps).items()):
self.stop_extension(name, apps)
del self.extension_apps[name]
12 changes: 12 additions & 0 deletions jupyter_server/serverapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1921,6 +1921,17 @@ def cleanup_terminals(self):
self.log.info(terminal_msg % n_terminals)
run_sync(terminal_manager.terminate_all())

def cleanup_extensions(self):
"""Call shutdown hooks in all extensions."""
n_extensions = len(self.extension_manager.extension_apps)
extension_msg = trans.ngettext(
'Shutting down %d extension',
'Shutting down %d extensions',
n_extensions
)
self.log.info(extension_msg % n_extensions)
self.extension_manager.stop_all_extensions(self)

def running_server_info(self, kernel_count=True):
"Return the current working directory and the server url information"
info = self.contents_manager.info_string() + "\n"
Expand Down Expand Up @@ -2152,6 +2163,7 @@ def _cleanup(self):
self.remove_browser_open_files()
self.cleanup_kernels()
self.cleanup_terminals()
self.cleanup_extensions()

def start_ioloop(self):
"""Start the IO Loop."""
Expand Down

0 comments on commit 7eb714e

Please sign in to comment.