Skip to content

Commit 5ea08b7

Browse files
committed
simplify server constructor
the constructor simply exects a root element that takes no arguments unless view parameters are passed by a query to the endpoint. in that case the element must accept an parameter passed in by the query.
1 parent bc5ec8b commit 5ea08b7

File tree

11 files changed

+147
-128
lines changed

11 files changed

+147
-128
lines changed

Diff for: docs/main.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ def prod():
6969
)
7070

7171

72-
def local():
72+
def local(path=""):
7373
import webbrowser
7474

7575
thread = server.daemon("127.0.0.1", 5000)
76-
webbrowser.open("http://127.0.0.1:5000/")
76+
webbrowser.open(f"http://127.0.0.1:5000/{path}")
7777
thread.join()
7878

7979

Diff for: idom/core/element.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,6 @@ def __repr__(self) -> str:
7979
args = sig.bind(*self._args, **self._kwargs).arguments
8080
items = ", ".join(f"{k}={v!r}" for k, v in args.items())
8181
if items:
82-
return f"{self._function.__name__}:{id(self)}({items})"
82+
return f"{self._function.__name__}({id(self)}, {items})"
8383
else:
84-
return f"{self._function.__name__}:{id(self)}()"
84+
return f"{self._function.__name__}({id(self)})"

Diff for: idom/core/layout.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
NamedTuple,
99
Any,
1010
Set,
11-
Optional,
1211
Iterator,
1312
AsyncIterator,
1413
Union,
@@ -64,9 +63,7 @@ class Layout(HasAsyncResources):
6463
if not hasattr(abc.ABC, "__weakref__"): # pragma: no cover
6564
__slots__.append("__weakref__")
6665

67-
def __init__(
68-
self, root: "AbstractElement", loop: Optional[asyncio.AbstractEventLoop] = None
69-
) -> None:
66+
def __init__(self, root: "AbstractElement") -> None:
7067
super().__init__()
7168
if not isinstance(root, AbstractElement):
7269
raise TypeError("Expected an AbstractElement, not %r" % root)

Diff for: idom/server/base.py

+26-28
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import TypeVar, Dict, Any, Tuple, Type, Optional, Generic, TypeVar
44
from threading import Thread
55

6-
from idom.core.element import ElementConstructor, AbstractElement
6+
from idom.core.element import ElementConstructor
77
from idom.core.layout import Layout, Layout
88
from idom.core.dispatcher import (
99
AbstractDispatcher,
@@ -34,11 +34,17 @@ class AbstractRenderServer(Generic[_App, _Config]):
3434
_dispatcher_type: Type[AbstractDispatcher]
3535
_layout_type: Type[Layout] = Layout
3636

37-
def __init__(self, constructor: ElementConstructor) -> None:
37+
def __init__(
38+
self,
39+
constructor: ElementConstructor,
40+
config: Optional[_Config] = None,
41+
) -> None:
3842
self._app: Optional[_App] = None
39-
self._root_element_constructor = constructor
43+
self._make_root_element = constructor
4044
self._daemonized = False
4145
self._config = self._init_config()
46+
if config is not None:
47+
self._config = self._update_config(self._config, config)
4248

4349
@property
4450
def loop(self) -> AbstractEventLoop:
@@ -78,15 +84,12 @@ def register(self: _Self, app: Optional[_App]) -> _Self:
7884
self._app = app
7985
return self
8086

81-
def configure(self: _Self, config: Optional[_Config] = None) -> _Self:
82-
"""Configure this extension."""
83-
if config is not None:
84-
self._config = self._update_config(self._config, config)
85-
return self
86-
87-
@abc.abstractmethod
8887
def stop(self) -> None:
8988
"""Stop the running application"""
89+
self.loop.call_soon_threadsafe(self._stop)
90+
91+
@abc.abstractmethod
92+
def _stop(self):
9093
raise NotImplementedError()
9194

9295
@abc.abstractmethod
@@ -109,15 +112,6 @@ def _run_application(
109112
raise NotImplementedError()
110113

111114
@abc.abstractmethod
112-
async def _run_dispatcher(
113-
self,
114-
send: SendCoroutine,
115-
recv: RecvCoroutine,
116-
parameters: Dict[str, Any],
117-
loop: Optional[AbstractEventLoop] = None,
118-
) -> None:
119-
raise NotImplementedError()
120-
121115
def _update_config(self, old: _Config, new: _Config) -> _Config: # pragma: no cover
122116
"""Return the new configuration options
123117
@@ -127,19 +121,23 @@ def _update_config(self, old: _Config, new: _Config) -> _Config: # pragma: no c
127121
"""
128122
raise NotImplementedError()
129123

124+
async def _run_dispatcher(
125+
self,
126+
send: SendCoroutine,
127+
recv: RecvCoroutine,
128+
params: Dict[str, Any],
129+
) -> None:
130+
async with self._make_dispatcher(params) as dispatcher:
131+
await dispatcher.run(send, recv, None)
132+
130133
def _make_dispatcher(
131134
self,
132-
parameters: Dict[str, Any],
133-
loop: Optional[AbstractEventLoop] = None,
135+
params: Dict[str, Any],
134136
) -> AbstractDispatcher:
135-
return self._dispatcher_type(self._make_layout(parameters, loop))
137+
return self._dispatcher_type(self._make_layout(params))
136138

137139
def _make_layout(
138140
self,
139-
parameters: Dict[str, Any],
140-
loop: Optional[AbstractEventLoop] = None,
141+
params: Dict[str, Any],
141142
) -> Layout:
142-
return self._layout_type(self._make_root_element(parameters), loop)
143-
144-
def _make_root_element(self, parameters: Dict[str, Any]) -> AbstractElement:
145-
return self._root_element_constructor(**parameters)
143+
return self._layout_type(self._make_root_element(**params))

Diff for: idom/server/sanic.py

+7-18
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Config(TypedDict, total=False):
3131
class SanicRenderServer(AbstractRenderServer[Sanic, Config]):
3232
"""Base ``sanic`` extension."""
3333

34-
def stop(self) -> None:
34+
def _stop(self) -> None:
3535
self.application.stop()
3636

3737
def _init_config(self) -> Config:
@@ -74,8 +74,8 @@ async def sock_recv() -> LayoutEvent:
7474
event = message["body"]["event"]
7575
return LayoutEvent(event["target"], event["data"])
7676

77-
param_dict = {k: request.args.get(k) for k in request.args}
78-
await self._run_dispatcher(sock_send, sock_recv, param_dict)
77+
element_params = {k: request.args.get(k) for k in request.args}
78+
await self._run_dispatcher(sock_send, sock_recv, element_params)
7979

8080
def handler_name(function: Any) -> str:
8181
return f"{blueprint.name}.{function.__name__}"
@@ -141,16 +141,6 @@ class PerClientStateServer(SanicRenderServer):
141141

142142
_dispatcher_type = SingleViewDispatcher
143143

144-
async def _run_dispatcher(
145-
self,
146-
send: SendCoroutine,
147-
recv: RecvCoroutine,
148-
parameters: Dict[str, Any],
149-
loop: Optional[asyncio.AbstractEventLoop] = None,
150-
) -> None:
151-
async with self._make_dispatcher(parameters, loop) as dispatcher:
152-
await dispatcher.run(send, recv, None)
153-
154144

155145
class SharedClientStateServer(SanicRenderServer):
156146
"""All connected client views will have shared state."""
@@ -166,7 +156,7 @@ def _setup_application(self, app: Sanic, config: Config) -> None:
166156
async def _activate_dispatcher(
167157
self, app: Sanic, loop: asyncio.AbstractEventLoop
168158
) -> None:
169-
self._dispatcher = cast(SharedViewDispatcher, self._make_dispatcher({}, loop))
159+
self._dispatcher = cast(SharedViewDispatcher, self._make_dispatcher({}))
170160
await self._dispatcher.start()
171161

172162
async def _deactivate_dispatcher(
@@ -179,10 +169,9 @@ async def _run_dispatcher(
179169
self,
180170
send: SendCoroutine,
181171
recv: RecvCoroutine,
182-
parameters: Dict[str, Any],
183-
loop: Optional[asyncio.AbstractEventLoop] = None,
172+
params: Dict[str, Any],
184173
) -> None:
185-
if parameters:
186-
msg = f"SharedClientState server does not support per-client view parameters {parameters}"
174+
if params:
175+
msg = f"SharedClientState server does not support per-client view parameters {params}"
187176
raise ValueError(msg)
188177
await self._dispatcher.run(send, recv, uuid.uuid4().hex, join=True)

Diff for: idom/server/utils.py

+71-32
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,56 @@ def _find_default_server_type() -> Optional[Type[AbstractRenderServer[Any, Any]]
2020
pass
2121
else:
2222
return getattr(module, server_name)
23-
else:
23+
else: # pragma: no cover
2424
return None
2525

2626

2727
def run(
2828
element: ElementConstructor,
29-
server: Optional[
30-
Type[AbstractRenderServer[Any, Any]]
31-
] = _find_default_server_type(),
29+
server_type: Optional[Type[_S]] = _find_default_server_type(),
3230
host: Optional[str] = "127.0.0.1",
3331
port: Optional[int] = None,
32+
server_options: Optional[Any] = None,
3433
run_options: Optional[Dict[str, Any]] = None,
35-
) -> None:
36-
"""A utility for quickly running a view with minimal boilerplat"""
37-
if server is None:
34+
daemon: bool = False,
35+
app: Optional[Any] = None,
36+
) -> _S:
37+
"""A utility for quickly running a render server with minimal boilerplate
38+
39+
Parameters:
40+
element: The root of the view.
41+
server_type: What server to run. Defaults to a builtin implementation if available.
42+
host: The host string.
43+
port: The port number. Defaults to a dynamically discovered available port.
44+
server_options: Options passed to configure the server.
45+
run_options: Options passed to the server to run it.
46+
daemon: Whether the server should be run in a daemon thread.
47+
app: Register the server to an existing application and run that.
48+
49+
Returns:
50+
The server instance. This isn't really useful unless the server is spawned
51+
as a daemon. Otherwise this function blocks until the server has stopped.
52+
"""
53+
if server_type is None: # pragma: no cover
3854
raise ValueError("No default server available.")
39-
if port is None:
55+
if port is None: # pragma: no cover
4056
port = find_available_port(host)
41-
server(element).run(host, port, **(run_options or {}))
4257

58+
server = server_type(element, server_options)
4359

44-
def find_available_port(host: str) -> int:
45-
"""Get a port that's available for the given host"""
46-
sock = socket()
47-
sock.bind((host, 0))
48-
return cast(int, sock.getsockname()[1])
60+
if app is not None:
61+
server.register(app)
62+
63+
run_server = server.run if not daemon else server.daemon
64+
run_server(host, port, **(run_options or {}))
65+
66+
return server
4967

5068

5169
def multiview_server(
52-
server: Type[_S],
53-
host: str,
54-
port: int,
70+
server_type: Type[_S],
71+
host: Optional[str] = "127.0.0.1",
72+
port: Optional[int] = None,
5573
server_options: Optional[Any] = None,
5674
run_options: Optional[Dict[str, Any]] = None,
5775
app: Optional[Any] = None,
@@ -75,18 +93,25 @@ def multiview_server(
7593
See :func:`idom.widgets.common.multiview` for details.
7694
"""
7795
mount, element = multiview()
78-
server_instance = server(element)
79-
server_instance.configure(server_options)
80-
if app is not None:
81-
server_instance.register(app)
82-
server_instance.daemon(host, port, **(run_options or {}))
83-
return mount, server_instance
96+
97+
server = run(
98+
element,
99+
server_type,
100+
host,
101+
port,
102+
server_options=server_options,
103+
run_options=run_options,
104+
daemon=True,
105+
app=app,
106+
)
107+
108+
return mount, server
84109

85110

86111
def hotswap_server(
87-
server: Type[_S],
88-
host: str,
89-
port: int,
112+
server_type: Type[_S],
113+
host: Optional[str] = "127.0.0.1",
114+
port: Optional[int] = None,
90115
server_options: Optional[Any] = None,
91116
run_options: Optional[Dict[str, Any]] = None,
92117
sync_views: bool = True,
@@ -112,9 +137,23 @@ def hotswap_server(
112137
See :func:`idom.widgets.common.hotswap` for details.
113138
"""
114139
mount, element = hotswap(shared=sync_views)
115-
server_instance = server(element)
116-
server_instance.configure(server_options)
117-
if app is not None:
118-
server_instance.register(app)
119-
server_instance.daemon(host, port, **(run_options or {}))
120-
return mount, server_instance
140+
141+
server = run(
142+
element,
143+
server_type,
144+
host,
145+
port,
146+
server_options=server_options,
147+
run_options=run_options,
148+
daemon=True,
149+
app=app,
150+
)
151+
152+
return mount, server
153+
154+
155+
def find_available_port(host: str) -> int:
156+
"""Get a port that's available for the given host"""
157+
sock = socket()
158+
sock.bind((host, 0))
159+
return cast(int, sock.getsockname()[1])

0 commit comments

Comments
 (0)