Skip to content

Commit 9ac50db

Browse files
committed
update tests with changes in extension manager
1 parent 9ac399f commit 9ac50db

File tree

6 files changed

+46
-97
lines changed

6 files changed

+46
-97
lines changed

jupyter_server/extension/application.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,7 @@ def _load_jupyter_server_extension(cls, serverapp):
400400
extension_manager = serverapp.extension_manager
401401
try:
402402
# Get loaded extension from serverapp.
403-
pkg = extension_manager.enabled_extensions[cls.name]
404-
point = pkg.extension_points[cls.name]
403+
point = extension_manager.extension_points[cls.name]
405404
extension = point.app
406405
except KeyError:
407406
extension = cls()

jupyter_server/extension/manager.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
HasTraits,
66
Dict,
77
Unicode,
8-
Instance,
9-
default,
108
validate
119
)
1210

@@ -23,6 +21,7 @@ class ExtensionPoint(HasTraits):
2321
point defined by metadata and importable from a Python package.
2422
"""
2523
metadata = Dict()
24+
_app = None
2625

2726
@validate('metadata')
2827
def _valid_metadata(self, proposed):
@@ -43,10 +42,11 @@ def _valid_metadata(self, proposed):
4342
"The submodule '{}' could not be found. Are you "
4443
"sure the extension is installed?".format(self._module_name)
4544
)
46-
# Initialize the app object if it exists.
47-
app = self.metadata.get("app")
48-
if app:
49-
metadata["app"] = app()
45+
# If the metadata includes an ExtensionApp, create an instance.
46+
try:
47+
self._app = metadata.get("app")()
48+
except TypeError:
49+
pass
5050
return metadata
5151

5252
@property
@@ -56,7 +56,7 @@ def linked(self):
5656
@property
5757
def app(self):
5858
"""If the metadata includes an `app` field"""
59-
return self.metadata.get("app")
59+
return self._app
6060

6161
@property
6262
def module_name(self):
@@ -129,7 +129,7 @@ class ExtensionPackage(HasTraits):
129129
"""
130130
name = Unicode(help="Name of the an importable Python package.")
131131

132-
# A dictionary that stores whether the extension point has been linked.
132+
# Store extension points that have been linked.
133133
_linked_points = {}
134134

135135
@validate("name")
@@ -186,7 +186,7 @@ class ExtensionManager(LoggingConfigurable):
186186
m = ExtensionManager(jpserver_extensions=extensions)
187187
"""
188188
# The `enabled_extensions` attribute provides a dictionary
189-
# with extension names mapped to their ExtensionPackage interface
189+
# with extension (package) names mapped to their ExtensionPackage interface
190190
# (see above). This manager simplifies the interaction between the
191191
# ServerApp and the extensions being appended.
192192
_enabled_extensions = {}
@@ -203,6 +203,16 @@ def enabled_extensions(self):
203203
"""
204204
return dict(sorted(self._enabled_extensions.items()))
205205

206+
@property
207+
def extension_points(self):
208+
extensions = self.enabled_extensions
209+
return {
210+
name: point
211+
for value in extensions.values()
212+
for name, point in value.extension_points.items()
213+
}
214+
215+
206216
def from_jpserver_extensions(self, jpserver_extensions):
207217
"""Add extensions from 'jpserver_extensions'-like dictionary."""
208218
for name, enabled in jpserver_extensions.items():

tests/extension/mockextensions/app.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,4 @@ class MockExtensionApp(ExtensionAppJinjaMixin, ExtensionApp):
3737
def initialize_handlers(self):
3838
self.handlers.append(('/mock', MockExtensionHandler))
3939
self.handlers.append(('/mock_template', MockExtensionTemplateHandler))
40-
self.loaded = True
41-
40+
self.loaded = True

tests/extension/test_app.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import pytest
22
from jupyter_server.serverapp import ServerApp
33

4+
# Use ServerApps environment because it monkeypatches
5+
# jupyter_core.paths and provides a config directory
6+
# that's not cross contaminating the user config directory.
7+
pytestmark = pytest.mark.usefixtures("environ")
8+
49

510
@pytest.fixture
611
def server_config(request, template_dir):
@@ -21,14 +26,19 @@ def server_config(request, template_dir):
2126

2227

2328
@pytest.fixture
24-
def mock_extension(extension_manager):
25-
return extension_manager.extension_points["mockextension"].app
29+
def mock_extension(serverapp, extension_manager):
30+
name = "tests.extension.mockextensions"
31+
pkg = extension_manager.enabled_extensions[name]
32+
point = pkg.extension_points["mockextension"]
33+
app = point.app
34+
return app
2635

2736

28-
def test_initialize(mock_extension, template_dir):
37+
def test_initialize(serverapp, mock_extension, template_dir):
2938
# Check that settings and handlers were added to the mock extension.
3039
assert isinstance(mock_extension.serverapp, ServerApp)
3140
assert len(mock_extension.handlers) > 0
41+
assert mock_extension.loaded
3242
assert mock_extension.template_paths == [str(template_dir)]
3343

3444

@@ -46,22 +56,20 @@ def test_instance_creation_with_argv(
4656
serverapp,
4757
trait_name,
4858
trait_value,
49-
extension_manager
59+
mock_extension,
5060
):
51-
extension = extension_manager.extension_points['mockextension'].app
52-
assert getattr(extension, trait_name) == trait_value
61+
assert getattr(mock_extension, trait_name) == trait_value
5362

5463

5564
def test_extensionapp_load_config_file(
5665
extension_environ,
5766
config_file,
58-
extension_manager,
67+
mock_extension,
5968
serverapp,
6069
):
61-
extension = extension_manager.extension_points['mockextension'].app
6270
# Assert default config_file_paths is the same in the app and extension.
63-
assert extension.config_file_paths == serverapp.config_file_paths
64-
assert extension.config_dir == serverapp.config_dir
65-
assert extension.config_file_name == 'jupyter_mockextension_config'
71+
assert mock_extension.config_file_paths == serverapp.config_file_paths
72+
assert mock_extension.config_dir == serverapp.config_dir
73+
assert mock_extension.config_file_name == 'jupyter_mockextension_config'
6674
# Assert that the trait is updated by config file
67-
assert extension.mock_trait == 'config from file'
75+
assert mock_extension.mock_trait == 'config from file'

tests/extension/test_manager.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,11 @@ def test_extension_package_notfound_error():
6262

6363

6464
def test_extension_manager_api():
65-
# Import mock extension metadata
66-
from .mockextensions import _jupyter_server_extension_paths
67-
68-
# Testing the first path (which is an extension app).
69-
metadata_list = _jupyter_server_extension_paths()
70-
7165
jpserver_extensions = {
7266
"tests.extension.mockextensions": True
7367
}
74-
manager = ExtensionManager(jpserver_extensions=jpserver_extensions)
75-
assert len(manager.extensions) == 1
76-
assert len(manager.extension_points) == len(metadata_list)
77-
assert "mockextension" in manager.extension_points
78-
assert "tests.extension.mockextensions.mock1" in manager.extension_points
68+
manager = ExtensionManager()
69+
manager.from_jpserver_extensions(jpserver_extensions)
70+
assert len(manager.enabled_extensions) == 1
71+
assert "tests.extension.mockextensions" in manager.enabled_extensions
7972

tests/extension/test_utils.py

Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,5 @@
11
import pytest
2-
from jupyter_server.extension.utils import (
3-
list_extensions_in_configd,
4-
configd_enabled,
5-
validate_extension
6-
)
7-
8-
# Use ServerApps environment because it monkeypatches
9-
# jupyter_core.paths and provides a config directory
10-
# that's not cross contaminating the user config directory.
11-
pytestmark = pytest.mark.usefixtures("environ")
12-
13-
14-
@pytest.fixture
15-
def configd(env_config_path):
16-
"""A pathlib.Path object that acts like a jupyter_server_config.d folder."""
17-
configd = env_config_path.joinpath('jupyter_server_config.d')
18-
configd.mkdir()
19-
return configd
20-
21-
22-
ext1_json_config = """\
23-
{
24-
"ServerApp": {
25-
"jpserver_extensions": {
26-
"ext1_config": true
27-
}
28-
}
29-
}
30-
"""
31-
32-
@pytest.fixture
33-
def ext1_config(configd):
34-
config = configd.joinpath("ext1_config.json")
35-
config.write_text(ext1_json_config)
36-
37-
38-
ext2_json_config = """\
39-
{
40-
"ServerApp": {
41-
"jpserver_extensions": {
42-
"ext2_config": false
43-
}
44-
}
45-
}
46-
"""
47-
48-
49-
@pytest.fixture
50-
def ext2_config(configd):
51-
config = configd.joinpath("ext2_config.json")
52-
config.write_text(ext2_json_config)
53-
54-
55-
def test_list_extension_from_configd(ext1_config, ext2_config):
56-
extensions = list_extensions_in_configd()
57-
assert "ext2_config" in extensions
58-
assert "ext1_config" in extensions
59-
60-
61-
def test_config_enabled(ext1_config):
62-
assert configd_enabled("ext1_config")
2+
from jupyter_server.extension.utils import validate_extension
633

644

655
def test_validate_extension():

0 commit comments

Comments
 (0)