Skip to content

Commit 1e5b811

Browse files
committed
ExtensionManager: load default config manager by default
- use traitlets for defaults, loading config_managers - load default config manager with default config path by default - remove semi-private attributes transparently exposed with public getters
1 parent 5e77b73 commit 1e5b811

File tree

2 files changed

+54
-37
lines changed

2 files changed

+54
-37
lines changed

jupyter_server/extension/manager.py

+43-37
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import importlib
22

3-
from traitlets.config import LoggingConfigurable, Config
3+
from traitlets.config import LoggingConfigurable
44

55
from traitlets import (
66
HasTraits,
77
Dict,
88
Unicode,
99
Bool,
1010
Any,
11-
validate
11+
Instance,
12+
default,
13+
observe,
14+
validate,
1215
)
1316

17+
from .config import ExtensionConfigManager
1418
from .utils import (
1519
ExtensionMetadataError,
1620
ExtensionModuleNotFound,
@@ -238,35 +242,44 @@ class ExtensionManager(LoggingConfigurable):
238242
linking, loading, and managing Jupyter Server extensions.
239243
240244
Usage:
241-
m = ExtensionManager(jpserver_extensions=extensions)
245+
m = ExtensionManager(config_manager=...)
242246
"""
243-
def __init__(self, config_manager=None, *args, **kwargs):
244-
super().__init__(*args, **kwargs)
245-
# The `enabled_extensions` attribute provides a dictionary
246-
# with extension (package) names mapped to their ExtensionPackage interface
247-
# (see above). This manager simplifies the interaction between the
248-
# ServerApp and the extensions being appended.
249-
self._extensions = {}
250-
# The `_linked_extensions` attribute tracks when each extension
251-
# has been successfully linked to a ServerApp. This helps prevent
252-
# extensions from being re-linked recursively unintentionally if another
253-
# extension attempts to link extensions again.
254-
self._linked_extensions = {}
255-
self._config_manager = config_manager
256-
if self._config_manager:
257-
self.from_config_manager(self._config_manager)
258247

259-
@property
260-
def config_manager(self):
261-
return self._config_manager
248+
config_manager = Instance(ExtensionConfigManager, allow_none=True)
249+
250+
@default("config_manager")
251+
def _load_default_config_manager(self):
252+
config_manager = ExtensionConfigManager()
253+
self.from_config_manager(config_manager)
254+
return config_manager
255+
256+
@observe("config_manager")
257+
def _config_manager_changed(self, change):
258+
if change.new:
259+
self.from_config_manager(config_manager)
260+
261+
# The `extensions` attribute provides a dictionary
262+
# with extension (package) names mapped to their ExtensionPackage interface
263+
# (see above). This manager simplifies the interaction between the
264+
# ServerApp and the extensions being appended.
265+
extensions = Dict(
266+
help="""
267+
Dictionary with extension package names as keys
268+
and ExtensionPackage objects as values.
269+
"""
270+
)
262271

263-
@property
264-
def extensions(self):
265-
"""Dictionary with extension package names as keys
266-
and an ExtensionPackage objects as values.
272+
# The `_linked_extensions` attribute tracks when each extension
273+
# has been successfully linked to a ServerApp. This helps prevent
274+
# extensions from being re-linked recursively unintentionally if another
275+
# extension attempts to link extensions again.
276+
linked_extensions = Dict(
277+
help="""
278+
Dictionary with extension names as keys
279+
280+
values are True if the extension is linked, False if not.
267281
"""
268-
# Sort enabled extensions before
269-
return self._extensions
282+
)
270283

271284
@property
272285
def extension_points(self):
@@ -277,15 +290,8 @@ def extension_points(self):
277290
for name, point in value.extension_points.items()
278291
}
279292

280-
@property
281-
def linked_extensions(self):
282-
"""Dictionary with extension names as keys; values are
283-
True if the extension is linked, False if not."""
284-
return self._linked_extensions
285-
286293
def from_config_manager(self, config_manager):
287294
"""Add extensions found by an ExtensionConfigManager"""
288-
self._config_manager = config_manager
289295
jpserver_extensions = self._config_manager.get_jpserver_extensions()
290296
self.from_jpserver_extensions(jpserver_extensions)
291297

@@ -300,21 +306,21 @@ def add_extension(self, extension_name, enabled=False):
300306
"""
301307
try:
302308
extpkg = ExtensionPackage(name=extension_name, enabled=enabled)
303-
self._extensions[extension_name] = extpkg
309+
self.extensions[extension_name] = extpkg
304310
return True
305311
# Raise a warning if the extension cannot be loaded.
306312
except Exception as e:
307313
self.log.warning(e)
308314
return False
309315

310316
def link_extension(self, name, serverapp):
311-
linked = self._linked_extensions.get(name, False)
317+
linked = self.linked_extensions.get(name, False)
312318
extension = self.extensions[name]
313319
if not linked and extension.enabled:
314320
try:
315321
# Link extension and store links
316322
extension.link_all_points(serverapp)
317-
self._linked_extensions[name] = True
323+
self.linked_extensions[name] = True
318324
self.log.info("{name} | extension was successfully linked.".format(name=name))
319325
except Exception as e:
320326
self.log.warning(e)

jupyter_server/tests/extension/test_manager.py

+11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
import os
2+
13
import pytest
4+
5+
from jupyter_core.paths import jupyter_config_path
6+
27
from jupyter_server.extension.manager import (
38
ExtensionPoint,
49
ExtensionPackage,
@@ -66,11 +71,17 @@ def test_extension_package_notfound_error():
6671
ExtensionPackage(name="nonexistent")
6772

6873

74+
def _normalize_path(path_list):
75+
return [p.rstrip(os.path.sep) for p in path_list]
76+
77+
6978
def test_extension_manager_api():
7079
jpserver_extensions = {
7180
"jupyter_server.tests.extension.mockextensions": True
7281
}
7382
manager = ExtensionManager()
83+
assert manager.config_manager
84+
assert _normalize_path(manager.config_manager.read_config_path) == _normalize_path(jupyter_config_path())
7485
manager.from_jpserver_extensions(jpserver_extensions)
7586
assert len(manager.extensions) == 1
7687
assert "jupyter_server.tests.extension.mockextensions" in manager.extensions

0 commit comments

Comments
 (0)