Skip to content

Commit 6ffbafd

Browse files
committed
no copy of defined web modules
instead use a shim where we look up registered file paths
1 parent 45bb783 commit 6ffbafd

File tree

8 files changed

+90
-56
lines changed

8 files changed

+90
-56
lines changed

.pre-commit-config.yaml

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ repos:
33
rev: stable
44
hooks:
55
- id: black
6-
language_version: python3.6
76
- repo: https://github.com/PyCQA/flake8
87
rev: 3.7.9
98
hooks:

docs/source/widgets/custom_chart.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66

77
here = Path(__file__).parent
8-
with (here / "custom_chart.js").open() as f:
9-
ClickableChart = idom.Module("chart", source=f).Import("ClickableChart")
8+
custom_chart_path = here / "custom_chart.js"
9+
ClickableChart = idom.Module("chart", source=custom_chart_path).Import("ClickableChart")
1010

1111

1212
data = [

idom/client/__init__.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
from .manage import (
2-
web_module,
2+
web_module_url,
3+
find_path,
4+
delete_web_modules,
5+
register_web_module,
36
web_module_exists,
47
web_module_path,
5-
define_web_module,
6-
delete_web_modules,
78
install,
89
installed,
910
restore,
1011
)
1112

1213
__all__ = [
13-
"web_module",
14+
"web_module_url",
15+
"find_path",
16+
"delete_web_modules",
17+
"register_web_module",
1418
"web_module_exists",
1519
"web_module_path",
16-
"define_web_module",
17-
"delete_web_modules",
1820
"install",
1921
"installed",
2022
"restore",

idom/client/manage.py

+43-27
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,68 @@
1515
NODE_MODULES = STATIC_DIR / "node_modules"
1616
WEB_MODULES = STATIC_DIR / "web_modules"
1717

18+
STATIC_SHIMS: Dict[str, Path] = {}
1819

19-
def web_module(name: str) -> str:
20+
21+
def find_path(url_path: str) -> Optional[Path]:
22+
url_path = url_path.strip("/")
23+
24+
builtin_path = STATIC_DIR.joinpath(*url_path.split("/"))
25+
if builtin_path.exists():
26+
return builtin_path
27+
28+
return STATIC_SHIMS.get(url_path)
29+
30+
31+
def web_module_path(name: str) -> Optional[Path]:
32+
return find_path(f"web_modules/{name}.js")
33+
34+
35+
def web_module_url(name: str) -> str:
2036
path = f"../{WEB_MODULES.name}/{name}.js"
2137
if not web_module_exists(name):
2238
raise ValueError(f"Module '{path}' does not exist.")
2339
return path
2440

2541

26-
def web_module_path(name: str) -> Optional[Path]:
27-
path = _create_web_module_os_path(name).with_suffix(".js")
28-
return path if path.is_file() else None
29-
30-
3142
def web_module_exists(name: str) -> bool:
32-
return web_module_path(name) is not None
43+
return find_path(f"web_modules/{name}.js") is not None
3344

3445

35-
def define_web_module(name: str, source: str) -> str:
36-
path = _create_web_module_os_path(name).with_suffix(".js")
37-
with path.open("w+") as f:
38-
f.write(source)
39-
return web_module(name)
46+
def register_web_module(name: str, source: Union[str, Path]) -> str:
47+
source_path = source if isinstance(source, Path) else Path(source)
48+
if web_module_exists(name):
49+
raise ValueError(f"Web module {name} already exists")
50+
if not source_path.is_file():
51+
raise ValueError(f"Web modules source {source} does not exist or is not a file")
52+
STATIC_SHIMS[f"web_modules/{name}.js"] = source
53+
return web_module_url(name)
4054

4155

4256
def delete_web_modules(names: Sequence[str], skip_missing: bool = False) -> None:
4357
paths = []
44-
for n in _to_list_of_str(names):
58+
for name in _to_list_of_str(names):
4559
exists = False
46-
path = _create_web_module_os_path(n)
47-
js_path = path.with_suffix(".js")
48-
if path.is_dir():
60+
61+
dir_name = f"web_modules/{name}"
62+
js_name = f"web_modules/{name}.js"
63+
path = find_path(dir_name)
64+
js_path = find_path(js_name)
65+
66+
if path is not None:
4967
paths.append(path)
5068
exists = True
51-
if js_path.is_file():
69+
70+
if js_name in STATIC_SHIMS:
71+
del STATIC_SHIMS[js_name]
72+
exists = True
73+
elif js_path is not None:
5274
paths.append(js_path)
5375
exists = True
76+
5477
if not exists and not skip_missing:
55-
raise ValueError(f"Module '{n}' does not exist.")
78+
raise ValueError(f"Module '{name}' does not exist.")
79+
5680
for p in paths:
5781
_delete_os_paths(p)
5882

@@ -109,6 +133,7 @@ def restore() -> None:
109133
_delete_os_paths(WEB_MODULES, NODE_MODULES)
110134
_run_subprocess(["npm", "install"], STATIC_DIR)
111135
_run_subprocess(["npm", "run", "snowpack"], STATIC_DIR)
136+
STATIC_SHIMS.clear()
112137

113138

114139
def _package_json() -> Dict[str, Any]:
@@ -141,15 +166,6 @@ def _run_subprocess(args: List[str], cwd: Union[str, Path]) -> None:
141166
return None
142167

143168

144-
def _create_web_module_os_path(name: str) -> Path:
145-
path = WEB_MODULES
146-
for n in name.split("/"):
147-
if not path.exists():
148-
path.mkdir()
149-
path /= n
150-
return path
151-
152-
153169
def _delete_os_paths(*paths: Path) -> None:
154170
for p in paths:
155171
if p.is_file():

idom/server/sanic.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
RecvCoroutine,
1717
)
1818
from idom.core.layout import LayoutEvent
19-
from idom.client.manage import STATIC_DIR
19+
from idom.client.manage import find_path
2020

2121
from .base import AbstractRenderServer
2222

@@ -88,10 +88,10 @@ async def client_files(
8888
request: request.Request, path: str
8989
) -> response.HTTPResponse:
9090
file_extensions = [".html", ".js", ".json"]
91-
abs_path = STATIC_DIR.joinpath(*path.split("/"))
91+
abs_path = find_path(path)
9292
return (
9393
(await response.file_stream(str(abs_path)))
94-
if abs_path.exists() and abs_path.suffix in file_extensions
94+
if abs_path is not None and abs_path.suffix in file_extensions
9595
else response.text(f"Could not find: {path!r}", status=404)
9696
)
9797

idom/widgets/utils.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from typing import Any, Callable, Tuple, Optional, Dict, Union, IO
1+
from pathlib import Path
2+
from typing import Any, Callable, Tuple, Optional, Dict, Union
23

34
from typing_extensions import Protocol
45

@@ -33,29 +34,28 @@ def __init__(
3334
self,
3435
name: str,
3536
install: Union[bool, str] = False,
36-
source: Optional[IO[str]] = None,
37+
source: Optional[Union[str, Path]] = None,
3738
replace: bool = False,
3839
) -> None:
3940
self._installed = False
4041
if install and source:
4142
raise ValueError("Both 'install' and 'source' were given.")
4243
elif (install or source) and not replace and client.web_module_exists(name):
43-
self._module = client.web_module(name)
44+
self._module = client.web_module_url(name)
4445
self._installed = True
4546
self._name = name
4647
elif source is not None:
47-
client.define_web_module(name, source.read())
48-
self._module = client.web_module(name)
48+
self._module = client.register_web_module(name, source)
4949
self._installed = True
5050
self._name = name
5151
elif isinstance(install, str):
5252
client.install([install], [name])
53-
self._module = client.web_module(name)
53+
self._module = client.web_module_url(name)
5454
self._installed = True
5555
self._name = name
5656
elif install is True:
5757
client.install(name)
58-
self._module = client.web_module(name)
58+
self._module = client.web_module_url(name)
5959
self._installed = True
6060
self._name = name
6161
else:
@@ -77,7 +77,7 @@ def Import(self, name: str, *args: Any, **kwargs: Any) -> "Import":
7777
def delete(self) -> None:
7878
if not self._installed:
7979
raise ValueError("Module is not installed locally")
80-
client.delete_web_modules([self._module])
80+
client.delete_web_modules([self._name])
8181

8282
def __repr__(self) -> str: # pragma: no cover
8383
return f"{type(self).__name__}({self._module!r})"

tests/test_client/test_manage.py

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from pathlib import Path
12
from subprocess import CalledProcessError
23
from unittest import mock
34

@@ -12,11 +13,17 @@
1213
def test_install():
1314
client.delete_web_modules(["jquery"], skip_missing=True)
1415
client.install("jquery")
16+
1517
assert client.web_module_exists("jquery")
18+
assert client.web_module_exists("/jquery") # works with a leading slash too
1619
assert "jquery" in client.installed()
1720
with assert_file_is_touched(client.web_module_path("jquery")):
1821
client.install("jquery", force=True)
19-
assert "jquery" in client.installed()
22+
23+
with pytest.raises(ValueError, match="already exists"):
24+
# can't register a module with the same name
25+
client.register_web_module("jquery", Path() / "some-module.js")
26+
2027
client.delete_web_modules("jquery")
2128
assert not client.web_module_exists("jquery")
2229
assert "jquery" not in client.installed()
@@ -27,12 +34,12 @@ def test_install_namespace_package():
2734
client.install("@material-ui/core")
2835
assert client.web_module_exists("@material-ui/core")
2936
expected = "../web_modules/@material-ui/core.js"
30-
assert client.web_module("@material-ui/core") == expected
37+
assert client.web_module_url("@material-ui/core") == expected
3138

3239

3340
def test_raise_on_missing_import_path():
3441
with pytest.raises(ValueError, match="does not exist"):
35-
client.web_module("module/that/does/not/exist")
42+
client.web_module_url("module/that/does/not/exist")
3643

3744

3845
called_process_error = CalledProcessError(1, "failing-cmd")
@@ -44,3 +51,11 @@ def test_bad_subprocess_call(subprocess_run, caplog):
4451
with pytest.raises(CalledProcessError):
4552
client.install(["victory"])
4653
assert "an error occured" in caplog.text
54+
55+
56+
def test_cannot_register_module_from_non_existant_source():
57+
with pytest.raises(ValueError, match="does not exist"):
58+
client.register_web_module("my-module", Path() / "a-non-existant-file.js")
59+
60+
with pytest.raises(ValueError, match="is not a file"):
61+
client.register_web_module("my-module", Path("/"))

tests/test_widgets/test_utils.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from io import StringIO
21
from pathlib import Path
32

43
import pytest
@@ -12,7 +11,7 @@
1211

1312
def test_module_cannot_have_source_and_install():
1413
with pytest.raises(ValueError, match=r"Both .* were given."):
15-
idom.Module("something", install="something", source=StringIO())
14+
idom.Module("something", install="something", source=HERE / "something.js")
1615

1716

1817
@pytest.fixture
@@ -27,7 +26,7 @@ def test_install(driver, display, victory):
2726
driver.find_element_by_class_name("VictoryContainer")
2827

2928
assert client.web_module_exists("victory")
30-
assert client.web_module("victory") == "../web_modules/victory.js"
29+
assert client.web_module_url("victory") == "../web_modules/victory.js"
3130

3231

3332
@pytest.mark.slow
@@ -41,16 +40,19 @@ def test_delete_module(victory):
4140

4241
@pytest.mark.slow
4342
def test_custom_module(driver, display, victory):
44-
with open(HERE / "my_chart.js") as f:
45-
my_chart = Module("my/chart", source=f)
43+
my_chart = Module("my/chart", source=HERE / "my_chart.js")
4644

4745
assert client.web_module_exists("my/chart")
48-
assert client.web_module("my/chart") == "../web_modules/my/chart.js"
46+
assert client.web_module_url("my/chart") == "../web_modules/my/chart.js"
4947

5048
display(my_chart.Import("Chart"))
5149

5250
driver.find_element_by_class_name("VictoryContainer")
5351

52+
my_chart.delete()
53+
54+
assert not client.web_module_exists("my/chart")
55+
5456

5557
def test_module_deletion():
5658
# also test install

0 commit comments

Comments
 (0)