Skip to content

Commit 9aa5183

Browse files
committed
module components should be accessible as attrs
This is just more intuitive that declaring them up-front
1 parent 6d16785 commit 9aa5183

9 files changed

+49
-40
lines changed

docs/source/examples/material_ui_button_no_action.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import idom
22

3-
material_ui = idom.install("@material-ui/core")
4-
MaterialButton = material_ui.define("Button", fallback="loading...")
3+
material_ui = idom.install("@material-ui/core", fallback="loading...")
54

65
idom.run(
76
idom.element(
8-
lambda: MaterialButton(
7+
lambda: material_ui.Button(
98
{"color": "primary", "variant": "contained"}, "Hello World!"
109
)
1110
)

docs/source/examples/material_ui_button_on_click.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
import idom
44

55

6-
material_ui = idom.install("@material-ui/core")
7-
MaterialButton = material_ui.define("Button", fallback="loading...")
6+
material_ui = idom.install("@material-ui/core", fallback="loading...")
87

98

109
@idom.element
1110
def ViewSliderEvents():
1211
event, set_event = idom.hooks.use_state(None)
1312

1413
return idom.html.div(
15-
MaterialButton(
14+
material_ui.Button(
1615
{
1716
"color": "primary",
1817
"variant": "contained",

docs/source/examples/material_ui_slider.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@
33
import idom
44

55

6-
material_ui = idom.install("@material-ui/core")
7-
MaterialSlider = material_ui.define("Slider", fallback="loading...")
6+
material_ui = idom.install("@material-ui/core", fallback="loading...")
87

98

109
@idom.element
1110
def ViewSliderEvents():
1211
event, set_event = idom.hooks.use_state(None)
1312

1413
return idom.html.div(
15-
MaterialSlider(
14+
material_ui.Slider(
1615
{
1716
"color": "primary",
1817
"step": 10,

docs/source/examples/simple_dashboard.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from idom.widgets.html import Input
77

88

9-
VictoryLine = idom.install("victory").define("VictoryLine", fallback="loading...")
9+
victory = idom.install("victory", fallback="loading...")
1010

1111

1212
@idom.element
@@ -53,7 +53,7 @@ async def animate():
5353
}
5454
set_data(data[1:] + [next_data_point])
5555

56-
return VictoryLine({"data": data, "style": {"parent": {"width": "500px"}}})
56+
return victory.VictoryLine({"data": data, "style": {"parent": {"width": "500px"}}})
5757

5858

5959
@idom.element

docs/source/examples/super_simple_chart.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44

55

66
path_to_source_file = Path(__file__).parent / "super_simple_chart.js"
7-
super_simple_chart = idom.Module("super-simple-chart", source_file=path_to_source_file)
8-
SuperSimpleChart = super_simple_chart.define("SuperSimpleChart")
7+
ssc = idom.Module("super-simple-chart", source_file=path_to_source_file)
98

109

1110
idom.run(
1211
idom.element(
13-
lambda: SuperSimpleChart(
12+
lambda: ssc.SuperSimpleChart(
1413
{
1514
"data": [
1615
{"x": 1, "y": 2},

docs/source/examples/victory_chart.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import idom
22

3-
victory = idom.install("victory")
4-
VictoryBar = victory.define("VictoryBar", fallback="loading...")
3+
victory = idom.install("victory", fallback="loading...")
54

65
idom.run(
76
idom.element(
8-
lambda: VictoryBar({"style": {"parent": {"width": "500px"}}}),
7+
lambda: victory.VictoryBar({"style": {"parent": {"width": "500px"}}}),
98
)
109
)

idom/client/app/package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

idom/client/module.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,27 @@
1010

1111

1212
@overload
13-
def install(packages: str) -> "Module":
13+
def install(
14+
packages: str,
15+
ignore_installed: bool,
16+
fallback: Optional[str],
17+
) -> "Module":
1418
...
1519

1620

1721
@overload
18-
def install(packages: Union[List[str], Tuple[str]]) -> List["Module"]:
22+
def install(
23+
packages: Union[List[str], Tuple[str]],
24+
ignore_installed: bool,
25+
fallback: Optional[str],
26+
) -> List["Module"]:
1927
...
2028

2129

2230
def install(
23-
packages: Union[str, List[str], Tuple[str]], ignore_installed: bool = False
31+
packages: Union[str, List[str], Tuple[str]],
32+
ignore_installed: bool = False,
33+
fallback: Optional[str] = None,
2434
) -> Union["Module", List["Module"]]:
2535
return_one = False
2636
if isinstance(packages, str):
@@ -38,7 +48,11 @@ def install(
3848
f"{client.current} failed to discover {list(not_discovered)}."
3949
)
4050

41-
return Module(pkg_names.pop()) if return_one else [Module(pkg) for pkg in pkg_names]
51+
return (
52+
Module(pkg_names.pop(), fallback=fallback)
53+
if return_one
54+
else [Module(pkg, fallback=fallback) for pkg in pkg_names]
55+
)
4256

4357

4458
class Module:
@@ -63,7 +77,7 @@ class Module:
6377
The URL this module will be imported from.
6478
"""
6579

66-
__slots__ = "url", "exports", "fallback"
80+
__slots__ = "url", "fallback", "exports", "_export_names"
6781

6882
def __init__(
6983
self,
@@ -73,25 +87,26 @@ def __init__(
7387
check_exports: bool = True,
7488
) -> None:
7589
self.fallback = fallback
76-
self.exports: Optional[List[str]] = None
90+
self._export_names: Optional[List[str]] = None
7791
if source_file is not None:
7892
self.url = (
7993
client.current.web_module_url(url_or_name)
8094
if client.current.web_module_exists(url_or_name)
8195
else client.current.add_web_module(url_or_name, source_file)
8296
)
8397
if check_exports:
84-
self.exports = client.current.web_module_exports(url_or_name)
98+
self._export_names = client.current.web_module_exports(url_or_name)
8599
elif client.current.web_module_exists(url_or_name):
86100
self.url = client.current.web_module_url(url_or_name)
87101
if check_exports:
88-
self.exports = client.current.web_module_exports(url_or_name)
102+
self._export_names = client.current.web_module_exports(url_or_name)
89103
elif _is_url(url_or_name):
90104
self.url = url_or_name
91105
else:
92106
raise ValueError(f"{url_or_name!r} is not installed or is not a URL")
107+
self.exports = {name: self.declare(name) for name in (self._export_names or [])}
93108

94-
def define(
109+
def declare(
95110
self,
96111
name: str,
97112
has_children: bool = True,
@@ -109,16 +124,19 @@ def define(
109124
this :class:`Module` instance.
110125
"""
111126
if (
112-
self.exports is not None
127+
self._export_names is not None
113128
# if 'default' is exported there's not much we can infer
114-
and "default" not in self.exports
129+
and "default" not in self._export_names
115130
):
116-
if name not in self.exports:
131+
if name not in self._export_names:
117132
raise ValueError(
118-
f"{self} does not export {name!r}, available options are {self.exports}"
133+
f"{self} does not export {name!r}, available options are {self._export_names}"
119134
)
120135
return Import(self.url, name, has_children, fallback=fallback or self.fallback)
121136

137+
def __getattr__(self, name: str) -> "Import":
138+
return self.exports.get(name) or self.declare(name)
139+
122140
def __repr__(self) -> str:
123141
return f"{type(self).__name__}({self.url})"
124142

tests/test_client/test_module.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def test_any_relative_or_abolute_url_allowed():
1616

1717
def test_module_import_repr():
1818
assert (
19-
repr(Module("/absolute/url/module").define("SomeComponent"))
19+
repr(Module("/absolute/url/module").declare("SomeComponent"))
2020
== "Import(name='SomeComponent', source='/absolute/url/module', fallback=None)"
2121
)
2222

@@ -27,7 +27,7 @@ def test_module_does_not_exist():
2727

2828

2929
def test_installed_module(driver, display, victory):
30-
display(victory.define("VictoryBar"))
30+
display(victory.VictoryBar)
3131
driver.find_element_by_class_name("VictoryContainer")
3232

3333

@@ -79,25 +79,21 @@ def add_web_module(self, package_name, source):
7979

8080
fake = Module("fake-name")
8181
assert fake.url == "./mock/url/to/module-fake-name.js"
82-
assert fake.exports == ["x", "y", "z"]
82+
assert list(fake.exports) == ["x", "y", "z"]
8383
assert fake.fallback is None
8484

8585
with pytest.raises(ValueError, match="does not export 'DoesNotExist'"):
86-
fake.define("DoesNotExist")
87-
88-
for name in fake.exports:
89-
fake.define(name)
86+
fake.declare("DoesNotExist")
9087

9188

9289
def test_module_from_source(driver, driver_wait, display):
9390
test_module = Module("test-module", source_file=HERE / "test_js_module.js")
94-
test_button = test_module.define("TestButton")
9591

9692
response_data = idom.Ref(None)
9793

9894
@idom.element
9995
def ShowButton():
100-
return test_button(
96+
return test_module.TestButton(
10197
{
10298
"id": "test-button",
10399
"onClick": lambda event: response_data.set_current(event["data"]),

0 commit comments

Comments
 (0)