Skip to content

Commit 7b4ed16

Browse files
committed
remove ClientImplementation
now that the client-side JS mounting functions accept a importSourceUrl to define the import source URL prefix, we can get rid of this now
1 parent 0c04f15 commit 7b4ed16

File tree

9 files changed

+39
-132
lines changed

9 files changed

+39
-132
lines changed

docs/source/javascript-modules.rst

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,23 +106,6 @@ that can run directly in the browser. This means we can't use fancy syntax like
106106
.. example:: super_simple_chart
107107

108108

109-
Alternate Client Implementations
110-
--------------------------------
111-
112-
While it's possible to implement a whole-sale replacement for IDOM's built-in client by
113-
adhering to IDOM's :ref:`API <Package API>` and :ref:`Specifications`, the easiest way
114-
to implement a custom client is to create an object that adheres to the
115-
:class:`~idom.client.protocol.ClientImplementation` protocol and update the ``current``
116-
value of the :attr:`~idom.client.protocol.client_implementation` ref. An example of
117-
such an implementation can be seen in
118-
`IDOM's Jupyter Widgets <https://github.com/idom-team/idom-jupyter>`__.
119-
120-
The following describes the client implementation interface:
121-
122-
.. autoclass:: idom.client.protocol.ClientImplementation
123-
:noindex:
124-
125-
126109
.. Links
127110
.. =====
128111

idom/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from .widgets.utils import hotswap, multiview
2222

2323
from .client.module import Module, Import, install
24-
from .client.protocol import client_implementation as client
2524

2625
from .server.prefab import run
2726

idom/client/__init__.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +0,0 @@
1-
from .protocol import client_implementation
2-
from .module import Module, Import
3-
4-
5-
__all__ = ["client_implementation", "Module", "Import"]

idom/client/manage.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,13 @@ def build(packages_to_install: Sequence[str], clean_build: bool = False) -> None
113113

114114
shutil.copytree(temp_build_dir, BUILD_DIR, symlinks=True)
115115

116+
not_discovered = package_names_to_install.difference(web_module_names())
117+
if not_discovered:
118+
raise RuntimeError( # pragma: no cover
119+
f"Successfuly installed {list(package_names_to_install)} but "
120+
f"failed to discover {list(not_discovered)} post-install."
121+
)
122+
116123

117124
def _npm_install(packages: List[str], cwd: Path) -> None:
118125
_run_subprocess(["npm", "install"] + packages, cwd)

idom/client/module.py

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
from pathlib import Path
22
from typing import Any, Optional, Union, List, Tuple, Dict, overload
33
from urllib.parse import urlparse
4-
54
from idom.core.vdom import VdomDict, ImportSourceDict, make_vdom_constructor
65

7-
from . import manage as builtin_client
8-
from .protocol import client_implementation as client
6+
from . import manage
97
from .utils import get_package_name
108

119

@@ -39,14 +37,8 @@ def install(
3937

4038
pkg_names = {get_package_name(pkg) for pkg in packages}
4139

42-
if ignore_installed or pkg_names.difference(client.current.web_module_names()):
43-
builtin_client.build(packages)
44-
not_discovered = pkg_names.difference(client.current.web_module_names())
45-
if not_discovered:
46-
raise RuntimeError(
47-
f"Successfuly installed {list(pkg_names)} but client implementation "
48-
f"{client.current} failed to discover {list(not_discovered)}."
49-
)
40+
if ignore_installed or pkg_names.difference(manage.web_module_names()):
41+
manage.build(packages)
5042

5143
return (
5244
Module(pkg_names.pop(), fallback=fallback)
@@ -90,16 +82,16 @@ def __init__(
9082
self._export_names: Optional[List[str]] = None
9183
if source_file is not None:
9284
self.url = (
93-
client.current.web_module_url(url_or_name)
94-
if client.current.web_module_exists(url_or_name)
95-
else client.current.add_web_module(url_or_name, source_file)
85+
manage.web_module_url(url_or_name)
86+
if manage.web_module_exists(url_or_name)
87+
else manage.add_web_module(url_or_name, source_file)
9688
)
9789
if check_exports:
98-
self._export_names = client.current.web_module_exports(url_or_name)
99-
elif client.current.web_module_exists(url_or_name):
100-
self.url = client.current.web_module_url(url_or_name)
90+
self._export_names = manage.web_module_exports(url_or_name)
91+
elif manage.web_module_exists(url_or_name):
92+
self.url = manage.web_module_url(url_or_name)
10193
if check_exports:
102-
self._export_names = client.current.web_module_exports(url_or_name)
94+
self._export_names = manage.web_module_exports(url_or_name)
10395
elif _is_url(url_or_name):
10496
self.url = url_or_name
10597
else:

idom/client/protocol.py

Lines changed: 0 additions & 36 deletions
This file was deleted.

tests/conftest.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,6 @@ def create():
110110
d.quit()
111111

112112

113-
@pytest.fixture
114-
def client_implementation():
115-
original = idom.client.current
116-
try:
117-
yield idom.client
118-
finally:
119-
idom.client.current = original
120-
121-
122113
@pytest.fixture(scope="session")
123114
def driver_is_headless(pytestconfig: Config):
124115
return bool(pytestconfig.option.headless)

tests/general_utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1+
from contextlib import contextmanager
12
from functools import wraps
23
from weakref import ref
34

45

56
import idom
67

78

9+
@contextmanager
10+
def patch_slots_object(obj, attr, new_value):
11+
# we do this since `mock.patch..object attempts to use __dict__
12+
# which is not necessarilly present on an object with __slots__`
13+
old_value = getattr(obj, attr)
14+
setattr(obj, attr, new_value)
15+
try:
16+
yield new_value
17+
finally:
18+
setattr(obj, attr, old_value)
19+
20+
821
class HookCatcher:
922
"""Utility for capturing a LifeCycleHook from an element
1023

tests/test_client/test_module.py

Lines changed: 9 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import pytest
44
import idom
5-
from idom import Module, install
5+
from idom import Module
6+
7+
from tests.general_utils import patch_slots_object
68

79
HERE = Path(__file__).parent
810

@@ -31,20 +33,6 @@ def test_installed_module(driver, display, victory):
3133
driver.find_element_by_class_name("VictoryContainer")
3234

3335

34-
def test_install_checks_client_implementation(client_implementation):
35-
class MockClientImplementation:
36-
def web_module_names(self):
37-
return set()
38-
39-
client_implementation.current = MockClientImplementation()
40-
41-
with pytest.raises(
42-
RuntimeError,
43-
match=r"Successfuly installed .* but client implementation .* failed to discover .*",
44-
):
45-
install("jquery@3.5.0", ignore_installed=True)
46-
47-
4836
def test_reference_pre_installed_module(victory):
4937
assert victory.url == idom.Module("victory").url
5038

@@ -55,37 +43,6 @@ def test_module_from_url():
5543
assert jquery.url == url
5644

5745

58-
def test_module_uses_current_client_implementation(client_implementation):
59-
class MockClientImplementation:
60-
def web_module_url(self, package_name):
61-
return f"./mock/url/to/module-{package_name}.js"
62-
63-
def web_module_exports(self, package_name):
64-
return ["x", "y", "z"]
65-
66-
def web_module_exists(self, package_name):
67-
return package_name == "fake-name"
68-
69-
def web_module_names(self):
70-
raise NotImplementedError()
71-
72-
def web_module_path(self, package_name):
73-
raise NotImplementedError()
74-
75-
def add_web_module(self, package_name, source):
76-
raise NotImplementedError()
77-
78-
client_implementation.current = MockClientImplementation()
79-
80-
fake = Module("fake-name")
81-
assert fake.url == "./mock/url/to/module-fake-name.js"
82-
assert list(fake.exports) == ["x", "y", "z"]
83-
assert fake.fallback is None
84-
85-
with pytest.raises(ValueError, match="does not export 'DoesNotExist'"):
86-
fake.declare("DoesNotExist")
87-
88-
8946
def test_module_from_source(driver, driver_wait, display):
9047
test_module = Module("test-module", source_file=HERE / "test_js_module.js")
9148

@@ -106,3 +63,9 @@ def ShowButton():
10663
client_button = driver.find_element_by_id("test-button")
10764
client_button.click()
10865
driver_wait.until(lambda dvr: response_data.current == 10)
66+
67+
68+
def test_module_checks_export_names(victory):
69+
with patch_slots_object(victory, "_export_names", []):
70+
with pytest.raises(ValueError, match="does not export"):
71+
victory.decalare("VictoryBar")

0 commit comments

Comments
 (0)