Skip to content

Commit 8f82227

Browse files
committed
create well defined client implementation layer
now the internals of IDOM use idom.client.protocol.client_implementation where possible (e.g. idom.Module) in order to allow different client implementations to be patched in.
1 parent 7eabcbd commit 8f82227

File tree

12 files changed

+128
-90
lines changed

12 files changed

+128
-90
lines changed

idom/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
DistributionNotFound as _DistributionNotFound,
44
)
55

6+
from .utils import Ref, html_to_vdom
7+
68
from .core.element import element, Element
79
from .core.events import event, Events
810
from .core.layout import Layout
911
from .core.vdom import vdom, VdomDict
1012
from .core import hooks
1113

1214
from .widgets.html import html
13-
from .widgets.module import Module, Import
1415
from .widgets.utils import hotswap, multiview
1516

16-
from .utils import Ref, html_to_vdom
17+
from .client.module import Module, Import
18+
from .client.protocol import client_implementation as client
1719

1820
from . import server
1921
from . import widgets
@@ -59,4 +61,5 @@
5961
"html_to_vdom",
6062
"VdomDict",
6163
"widgets",
64+
"client",
6265
]

idom/client/__init__.py

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,5 @@
1-
from .manage import (
2-
web_module_url,
3-
find_path,
4-
delete_web_modules,
5-
register_web_module,
6-
web_module_exists,
7-
install,
8-
installed,
9-
restore,
10-
)
1+
from .protocol import client_implementation
2+
from .module import Module, Import
113

12-
__all__ = [
13-
"web_module_url",
14-
"find_path",
15-
"delete_web_modules",
16-
"register_web_module",
17-
"web_module_exists",
18-
"web_module_path",
19-
"install",
20-
"installed",
21-
"restore",
22-
]
4+
5+
__all__ = ["client_implementation", "Module", "Import"]

idom/widgets/module.py renamed to idom/client/module.py

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,67 +2,60 @@
22
from typing import Any, Optional, Union
33
from urllib.parse import urlparse
44

5-
from idom import client
65
from idom.core.vdom import VdomDict, ImportSourceDict, make_vdom_constructor
76

7+
from .protocol import client_implementation as client
8+
89

910
class Module:
1011
"""A Javascript module
1112
1213
Parameters:
13-
path:
14+
url_or_name:
1415
The URL to an ECMAScript module which exports React components
1516
(*with* a ``.js`` file extension) or name of a module installed in the
1617
built-in client application (*without* a ``.js`` file extension).
1718
source_file:
18-
Only applicable if running on the built-in client app. Dynamically install
19-
the code in the give file as a single-file module. The built-in client will
20-
inject this module adjacent to other installed modules which means they can
21-
be imported via a relative path (e.g. ``./some-other-installed-module.js``).
22-
23-
.. warning::
24-
25-
Do not use the ``source_file`` parameter if not running with the client app
26-
distributed with ``idom``.
27-
28-
Examples:
29-
.. testcode::
30-
31-
import idom
32-
19+
Only applicable if running on a client app which supports this feature.
20+
Dynamically install the code in the give file as a single-file module. The
21+
built-in client will inject this module adjacent to other installed modules
22+
which means they can be imported via a relative path like
23+
``./some-other-installed-module.js``.
24+
25+
Attributes:
26+
installed:
27+
Whether or not this module has been installed into the built-in client app.
28+
url:
29+
The URL this module will be imported from.
30+
31+
Notes:
32+
To allow for other client implementations, you can set the current client
33+
implementation
34+
following private methods to support serving dynamically registered source
35+
files or loading modules that have been installed by some other means:
3336
"""
3437

35-
__slots__ = "_module", "_installed"
38+
__slots__ = "url", "installed"
3639

3740
def __init__(
3841
self,
39-
path: str,
42+
url_or_name: str,
4043
source_file: Optional[Union[str, Path]] = None,
4144
) -> None:
42-
self._installed = False
45+
self.installed = False
4346
if source_file is not None:
44-
self._module = client.register_web_module(path, source_file)
45-
self._installed = True
46-
elif client.web_module_exists(path):
47-
self._module = client.web_module_url(path)
48-
self._installed = True
49-
elif not _is_url(path):
47+
self.url = client.current.register_web_module(url_or_name, source_file)
48+
self.installed = True
49+
elif client.current.web_module_exists(url_or_name):
50+
self.url = client.current.web_module_url(url_or_name)
51+
self.installed = True
52+
elif _is_url(url_or_name):
53+
self.url = url_or_name
54+
else:
5055
raise ValueError(
51-
f"{path!r} is not installed - "
56+
f"{url_or_name!r} is not installed - "
5257
"only installed modules can omit a file extension."
5358
)
54-
else:
55-
self._module = path
56-
57-
@property
58-
def installed(self) -> bool:
59-
"""Whether or not this module has been installed into the built-in client app."""
60-
return self._installed
61-
62-
@property
63-
def url(self) -> str:
64-
"""The path this module will be imported from"""
65-
return self._module
6659

6760
def Import(self, name: str, *args: Any, **kwargs: Any) -> "Import":
6861
"""Return an :class:`Import` for the given :class:`Module` and ``name``
@@ -76,10 +69,10 @@ def Import(self, name: str, *args: Any, **kwargs: Any) -> "Import":
7669
Where ``name`` is the given name, and ``module`` is the :attr:`Module.url` of
7770
this :class:`Module` instance.
7871
"""
79-
return Import(self._module, name, *args, **kwargs)
72+
return Import(self.url, name, *args, **kwargs)
8073

8174
def __repr__(self) -> str: # pragma: no cover
82-
return f"{type(self).__name__}({self._module!r})"
75+
return f"{type(self).__name__}({self.url!r})"
8376

8477

8578
class Import:

idom/client/protocol.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from pathlib import Path
2+
from typing import Union
3+
4+
from typing_extensions import Protocol
5+
6+
from idom import Ref
7+
8+
from . import manage
9+
10+
11+
class ClientImplementation(Protocol):
12+
"""A minimal set of functions required to use :class:`idom.widget.module.Module`"""
13+
14+
def register_web_module(self, name: str, source: Union[str, Path]) -> None:
15+
"""Add a module with the given ``name`` to the client using the given ``source``"""
16+
17+
def web_module_url(self, name: str) -> str:
18+
"""Return the URL to import the module with the given name."""
19+
20+
def web_module_exists(self, name: str) -> bool:
21+
"""Check if a module with the given name is installed"""
22+
23+
24+
client_implementation: Ref[ClientImplementation] = Ref(manage)
25+
"""The current client implementation used by :class:`idom.widgets.module.Module`"""

idom/widgets/__init__.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
from .utils import hotswap, multiview
2-
from .module import Module, Import
32
from .html import html, image, Input
43

54
__all__ = [
65
"node",
76
"hotswap",
87
"multiview",
98
"html",
10-
"Module",
11-
"Import",
129
"image",
1310
"Input",
1411
]

scripts/__init__.py

Whitespace-only changes.

scripts/all_examples.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
import idom
66
from idom.server.sanic import PerClientStateServer
77

8+
from scripts.install_doc_js_modules import install_doc_js_modules
9+
10+
11+
install_doc_js_modules()
12+
813
here = Path(__file__).parent
914
examples_dir = here.parent / "docs" / "source" / "examples"
1015
sys.path.insert(0, str(examples_dir))

scripts/install_doc_js_modules.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from idom.client.manage import install, installed
2+
3+
4+
def install_doc_js_modules(show_spinner=True):
5+
to_install = {
6+
"@material-ui/core",
7+
"victory",
8+
"semantic-ui-react",
9+
}.difference(installed())
10+
if to_install:
11+
install(to_install, [], show_spinner=show_spinner)
12+
13+
14+
if __name__ == "__main__":
15+
install_doc_js_modules()

scripts/local-docs.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
set -e
2-
sphinx-build -b html docs/source docs/build
2+
python scripts/install_doc_js_modules.py
3+
sphinx-build -E -b html docs/source docs/build
34
cd docs
45
python -c "import main; main.local()"
56
cd ../

scripts/one_example.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
from idom.widgets.utils import hotswap
55
from idom.server.sanic import PerClientStateServer
66

7+
from scripts.install_doc_js_modules import install_doc_js_modules
8+
9+
10+
install_doc_js_modules()
11+
712
here = Path(__file__).parent
813
examples_dir = here.parent / "docs" / "source" / "examples"
914
sys.path.insert(0, str(examples_dir))

0 commit comments

Comments
 (0)