Skip to content

Commit 18daa75

Browse files
committed
fixes thus far
1 parent ad279f5 commit 18daa75

File tree

9 files changed

+67
-62
lines changed

9 files changed

+67
-62
lines changed

src/idom/core/component.py

+40-21
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,84 @@
11
from __future__ import annotations
22

33
import inspect
4+
from collections.abc import Sequence
45
from functools import wraps
5-
from typing import Any, Callable, Dict, Optional, Tuple
6+
from typing import Any, Callable
67

7-
from .types import ComponentType, VdomDict
8+
from .types import Key, RenderResult
9+
from .vdom import flatten_children
810

911

10-
def component(
11-
function: Callable[..., ComponentType | VdomDict | str | None]
12-
) -> Callable[..., Component]:
12+
def component(function: Callable[..., RenderResult]) -> Callable[..., Component]:
1313
"""A decorator for defining a new component.
1414
1515
Parameters:
1616
function: The component's :meth:`idom.core.proto.ComponentType.render` function.
1717
"""
1818
sig = inspect.signature(function)
1919

20-
if "key" in sig.parameters and sig.parameters["key"].kind in (
21-
inspect.Parameter.KEYWORD_ONLY,
22-
inspect.Parameter.POSITIONAL_OR_KEYWORD,
23-
):
20+
if [
21+
param.name
22+
for param in sig.parameters.values()
23+
if param.kind is inspect.Parameter.POSITIONAL_OR_KEYWORD
24+
or param.kind is inspect.Parameter.POSITIONAL_ONLY
25+
]:
26+
raise TypeError(
27+
"Position-only and positional-or-keyword parameters are disallowed - "
28+
"use variable positional arguments to define whether this component has "
29+
"children and keyword-only to define its attributes."
30+
)
31+
32+
if "key" in sig.parameters:
2433
raise TypeError(
2534
f"Component render function {function} uses reserved parameter 'key'"
2635
)
2736

2837
@wraps(function)
29-
def constructor(*args: Any, key: Optional[Any] = None, **kwargs: Any) -> Component:
30-
return Component(function, key, args, kwargs, sig)
38+
def constructor(
39+
*children: Any, key: Key | None = None, **attributes: Any
40+
) -> Component:
41+
return Component(function, key, flatten_children(children), attributes, sig)
3142

3243
return constructor
3344

3445

3546
class Component:
3647
"""An object for rending component models."""
3748

38-
__slots__ = "__weakref__", "_func", "_args", "_kwargs", "_sig", "key", "type"
49+
__slots__ = (
50+
"__weakref__",
51+
"_func",
52+
"_children",
53+
"_attributes",
54+
"_sig",
55+
"key",
56+
"type",
57+
)
3958

4059
def __init__(
4160
self,
42-
function: Callable[..., ComponentType | VdomDict | str | None],
43-
key: Optional[Any],
44-
args: Tuple[Any, ...],
45-
kwargs: Dict[str, Any],
61+
function: Callable[..., RenderResult],
62+
key: Key | None,
63+
children: Sequence[Any],
64+
attributes: dict[str, Any],
4665
sig: inspect.Signature,
4766
) -> None:
4867
self.key = key
4968
self.type = function
50-
self._args = args
51-
self._kwargs = kwargs
69+
self._children = children
70+
self._attributes = attributes
5271
self._sig = sig
5372

54-
def render(self) -> ComponentType | VdomDict | str | None:
55-
return self.type(*self._args, **self._kwargs)
73+
def render(self) -> RenderResult:
74+
return self.type(*self._children, **self._attributes)
5675

5776
def should_render(self, new: Component) -> bool:
5877
return True
5978

6079
def __repr__(self) -> str:
6180
try:
62-
args = self._sig.bind(*self._args, **self._kwargs).arguments
81+
args = self._sig.bind(*self._children, **self._attributes).arguments
6382
except TypeError:
6483
return f"{self.type.__name__}(...)"
6584
else:

src/idom/core/types.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,16 @@ class ComponentType(Protocol):
6262
This is used to see if two component instances share the same definition.
6363
"""
6464

65-
def render(self) -> VdomDict | ComponentType | str | None:
65+
def render(self) -> RenderResult:
6666
"""Render the component's view model."""
6767

6868
def should_render(self: _OwnType, new: _OwnType) -> bool:
6969
"""Whether the new component instance should be rendered."""
7070

7171

72+
RenderResult = Union["VdomDict", ComponentType, str, None]
73+
74+
7275
_Render = TypeVar("_Render", covariant=True)
7376
_Event = TypeVar("_Event", contravariant=True)
7477

@@ -200,12 +203,12 @@ class EventHandlerType(Protocol):
200203

201204

202205
class VdomDictConstructor(Protocol):
203-
"""Standard function for constructing a :class:`VdomDict`"""
204-
205206
def __call__(
206207
self,
207-
*attributes_and_children: VdomAttributesAndChildren,
208-
key: str = ...,
209-
event_handlers: Optional[EventHandlerMapping] = ...,
208+
*children: VdomChild,
209+
key: str | int | None = None,
210+
event_handlers: EventHandlerMapping | None = None,
211+
import_source: ImportSourceDict | None = None,
212+
**attributes: Any,
210213
) -> VdomDict:
211214
...

src/idom/core/vdom.py

+2-17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
Key,
2121
VdomChild,
2222
VdomDict,
23+
VdomDictConstructor,
2324
VdomJson,
2425
)
2526

@@ -184,9 +185,7 @@ def vdom(
184185
return model
185186

186187

187-
def make_vdom_constructor(
188-
tag: str, allow_children: bool = True
189-
) -> _VdomDictConstructor:
188+
def make_vdom_constructor(tag: str, allow_children: bool = True) -> VdomDictConstructor:
190189
"""Return a constructor for VDOM dictionaries with the given tag name.
191190
192191
The resulting callable will have the same interface as :func:`vdom` but without its
@@ -311,17 +310,3 @@ def _is_single_child(value: Any) -> bool:
311310
class _EllipsisRepr:
312311
def __repr__(self) -> str:
313312
return "..."
314-
315-
316-
class _VdomDictConstructor(Protocol):
317-
def __call__(
318-
self,
319-
tag: str,
320-
/,
321-
*children: VdomChild,
322-
key: str | int | None = None,
323-
event_handlers: EventHandlerMapping | None = None,
324-
import_source: ImportSourceDict | None = None,
325-
**attributes: Any,
326-
) -> VdomDict:
327-
...

src/idom/sample.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
@component
99
def SampleApp() -> VdomDict:
1010
return html.div(
11-
{"id": "sample", "style": {"padding": "15px"}},
1211
html.h1("Sample Application"),
1312
html.p(
1413
"This is a basic application made with IDOM. Click ",
1514
html.a(
16-
{"href": "https://pypi.org/project/idom/", "target": "_blank"},
1715
"here",
16+
href="https://pypi.org/project/idom/",
17+
target="_blank",
1818
),
1919
" to learn more.",
2020
),
21+
id="sample",
22+
style={"padding": "15px"},
2123
)

src/idom/types.py

-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
RootComponentConstructor,
2121
State,
2222
VdomAttributes,
23-
VdomAttributesAndChildren,
2423
VdomChild,
2524
VdomChildren,
2625
VdomDict,
@@ -46,7 +45,6 @@
4645
"RootComponentConstructor",
4746
"State",
4847
"VdomAttributes",
49-
"VdomAttributesAndChildren",
5048
"VdomChild",
5149
"VdomChildren",
5250
"VdomDict",

tests/test_backend/test_common.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async def display(page, request):
3939
async def test_display_simple_hello_world(display: DisplayFixture):
4040
@idom.component
4141
def Hello():
42-
return idom.html.p({"id": "hello"}, ["Hello World"])
42+
return idom.html.p("Hello World", id="hello")
4343

4444
await display.show(Hello)
4545

@@ -56,11 +56,9 @@ async def test_display_simple_click_counter(display: DisplayFixture):
5656
def Counter():
5757
count, set_count = idom.hooks.use_state(0)
5858
return idom.html.button(
59-
{
60-
"id": "counter",
61-
"onClick": lambda event: set_count(lambda old_count: old_count + 1),
62-
},
6359
f"Count: {count}",
60+
id="counter",
61+
onClick=lambda event: set_count(lambda old_count: old_count + 1),
6462
)
6563

6664
await display.show(Counter)
@@ -85,7 +83,7 @@ async def test_use_connection(display: DisplayFixture):
8583
@idom.component
8684
def ShowScope():
8785
conn.current = idom.use_connection()
88-
return html.pre({"id": "scope"}, str(conn.current))
86+
return html.pre(str(conn.current), id="scope")
8987

9088
await display.show(ShowScope)
9189

@@ -99,7 +97,7 @@ async def test_use_scope(display: DisplayFixture):
9997
@idom.component
10098
def ShowScope():
10199
scope.current = idom.use_scope()
102-
return html.pre({"id": "scope"}, str(scope.current))
100+
return html.pre(str(scope.current), id="scope")
103101

104102
await display.show(ShowScope)
105103

@@ -147,7 +145,7 @@ async def test_use_request(display: DisplayFixture, hook_name):
147145
@idom.component
148146
def ShowRoute():
149147
hook_val.current = hook()
150-
return html.pre({"id": "hook"}, str(hook_val.current))
148+
return html.pre(str(hook_val.current), id="hook")
151149

152150
await display.show(ShowRoute)
153151

tests/test_core/test_component.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def ComponentWithVarArgsAndKwargs(*args, **kwargs):
4747
async def test_display_simple_hello_world(display: DisplayFixture):
4848
@idom.component
4949
def Hello():
50-
return idom.html.p({"id": "hello"}, ["Hello World"])
50+
return idom.html.p("Hello World", id="hello")
5151

5252
await display.show(Hello)
5353

tests/test_core/test_events.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def Input():
151151
async def on_key_down(value):
152152
pass
153153

154-
return idom.html.input({"onKeyDown": on_key_down, "id": "input"})
154+
return idom.html.input(on_key_down=on_key_down, id="input")
155155

156156
await display.show(Input)
157157

@@ -170,9 +170,9 @@ async def on_click(event):
170170
set_clicked(True)
171171

172172
if not clicked:
173-
return idom.html.button({"onClick": on_click, "id": "click"}, ["Click Me!"])
173+
return idom.html.button("Click Me!", on_click=on_click, id="click")
174174
else:
175-
return idom.html.p({"id": "complete"}, ["Complete"])
175+
return idom.html.p("Complete", id="complete")
176176

177177
await display.show(Button)
178178

tests/test_core/test_hooks.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,9 @@ async def on_change(event):
240240
set_message(event["target"]["value"])
241241

242242
if message is None:
243-
return idom.html.input({"id": "input", "onChange": on_change})
243+
return idom.html.input(id="input", on_change=on_change)
244244
else:
245-
return idom.html.p({"id": "complete"}, ["Complete"])
245+
return idom.html.p("Complete", id="complete")
246246

247247
await display.show(Input)
248248

0 commit comments

Comments
 (0)