Skip to content

Commit a0135ca

Browse files
authored
Merge pull request #502 from minrk/default-config-manager
ExtensionManager: load default config manager by default
2 parents ccb4e2d + 08bfe34 commit a0135ca

File tree

2 files changed

+60
-38
lines changed

2 files changed

+60
-38
lines changed

jupyter_server/extension/manager.py

+49-38
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,
@@ -240,35 +244,44 @@ class ExtensionManager(LoggingConfigurable):
240244
linking, loading, and managing Jupyter Server extensions.
241245
242246
Usage:
243-
m = ExtensionManager(jpserver_extensions=extensions)
247+
m = ExtensionManager(config_manager=...)
244248
"""
245-
def __init__(self, config_manager=None, *args, **kwargs):
246-
super().__init__(*args, **kwargs)
247-
# The `enabled_extensions` attribute provides a dictionary
248-
# with extension (package) names mapped to their ExtensionPackage interface
249-
# (see above). This manager simplifies the interaction between the
250-
# ServerApp and the extensions being appended.
251-
self._extensions = {}
252-
# The `_linked_extensions` attribute tracks when each extension
253-
# has been successfully linked to a ServerApp. This helps prevent
254-
# extensions from being re-linked recursively unintentionally if another
255-
# extension attempts to link extensions again.
256-
self._linked_extensions = {}
257-
self._config_manager = config_manager
258-
if self._config_manager:
259-
self.from_config_manager(self._config_manager)
260249

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

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

273286
@property
274287
def extension_points(self):
@@ -279,16 +292,14 @@ def extension_points(self):
279292
for name, point in value.extension_points.items()
280293
}
281294

282-
@property
283-
def linked_extensions(self):
284-
"""Dictionary with extension names as keys; values are
285-
True if the extension is linked, False if not."""
286-
return self._linked_extensions
287-
288295
def from_config_manager(self, config_manager):
289296
"""Add extensions found by an ExtensionConfigManager"""
290-
self._config_manager = config_manager
291-
jpserver_extensions = self._config_manager.get_jpserver_extensions()
297+
# load triggered via config_manager trait observer
298+
self.config_manager = config_manager
299+
300+
def _load_config_manager(self, config_manager):
301+
"""Actually load our config manager"""
302+
jpserver_extensions = config_manager.get_jpserver_extensions()
292303
self.from_jpserver_extensions(jpserver_extensions)
293304

294305
def from_jpserver_extensions(self, jpserver_extensions):
@@ -302,21 +313,21 @@ def add_extension(self, extension_name, enabled=False):
302313
"""
303314
try:
304315
extpkg = ExtensionPackage(name=extension_name, enabled=enabled)
305-
self._extensions[extension_name] = extpkg
316+
self.extensions[extension_name] = extpkg
306317
return True
307318
# Raise a warning if the extension cannot be loaded.
308319
except Exception as e:
309320
self.log.warning(e)
310321
return False
311322

312323
def link_extension(self, name, serverapp):
313-
linked = self._linked_extensions.get(name, False)
324+
linked = self.linked_extensions.get(name, False)
314325
extension = self.extensions[name]
315326
if not linked and extension.enabled:
316327
try:
317328
# Link extension and store links
318329
extension.link_all_points(serverapp)
319-
self._linked_extensions[name] = True
330+
self.linked_extensions[name] = True
320331
self.log.info("{name} | extension was successfully linked.".format(name=name))
321332
except Exception as e:
322333
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,
@@ -68,11 +73,17 @@ def test_extension_package_notfound_error():
6873
ExtensionPackage(name="nonexistent")
6974

7075

76+
def _normalize_path(path_list):
77+
return [p.rstrip(os.path.sep) for p in path_list]
78+
79+
7180
def test_extension_manager_api():
7281
jpserver_extensions = {
7382
"jupyter_server.tests.extension.mockextensions": True
7483
}
7584
manager = ExtensionManager()
85+
assert manager.config_manager
86+
assert _normalize_path(manager.config_manager.read_config_path) == _normalize_path(jupyter_config_path())
7687
manager.from_jpserver_extensions(jpserver_extensions)
7788
assert len(manager.extensions) == 1
7889
assert "jupyter_server.tests.extension.mockextensions" in manager.extensions

0 commit comments

Comments
 (0)