Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 437878f

Browse files
committedFeb 27, 2025·
Simplier interface for reactpy.Vdom
1 parent f68efa3 commit 437878f

File tree

6 files changed

+42
-49
lines changed

6 files changed

+42
-49
lines changed
 

‎src/reactpy/_html.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ def __getattr__(self, value: str) -> VdomConstructor:
194194
return self.__cache__[value]
195195

196196
self.__cache__[value] = Vdom(
197-
tagName=value, allow_children=value not in NO_CHILDREN_ALLOWED_SVG
197+
value, allow_children=value not in NO_CHILDREN_ALLOWED_SVG
198198
)
199199

200200
return self.__cache__[value]
@@ -282,8 +282,8 @@ class HtmlConstructor:
282282

283283
# ruff: noqa: N815
284284
__cache__: ClassVar[dict[str, VdomConstructor]] = {
285-
"script": Vdom(tagName="script", custom_constructor=_script),
286-
"fragment": Vdom(tagName="", custom_constructor=_fragment),
285+
"script": Vdom("script", custom_constructor=_script),
286+
"fragment": Vdom("", custom_constructor=_fragment),
287287
"svg": SvgConstructor(),
288288
}
289289

@@ -294,7 +294,7 @@ def __getattr__(self, value: str) -> VdomConstructor:
294294
return self.__cache__[value]
295295

296296
self.__cache__[value] = Vdom(
297-
tagName=value, allow_children=value not in NO_CHILDREN_ALLOWED_HTML_BODY
297+
value, allow_children=value not in NO_CHILDREN_ALLOWED_HTML_BODY
298298
)
299299

300300
return self.__cache__[value]

‎src/reactpy/core/vdom.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from __future__ import annotations
33

44
import json
5-
from collections.abc import Iterable, Mapping, Sequence
5+
from collections.abc import Mapping, Sequence
66
from typing import (
77
Any,
88
Callable,
@@ -11,7 +11,6 @@
1111
)
1212

1313
from fastjsonschema import compile as compile_json_schema
14-
from typing_extensions import Unpack
1514

1615
from reactpy._warnings import warn
1716
from reactpy.config import REACTPY_CHECK_JSON_ATTRS, REACTPY_DEBUG
@@ -23,11 +22,11 @@
2322
EllipsisRepr,
2423
EventHandlerDict,
2524
EventHandlerType,
25+
ImportSourceDict,
2626
VdomAttributes,
2727
VdomChildren,
2828
VdomDict,
2929
VdomJson,
30-
VdomTypeDict,
3130
)
3231

3332
VDOM_JSON_SCHEMA = {
@@ -112,30 +111,29 @@ def is_vdom(value: Any) -> bool:
112111

113112

114113
class Vdom:
115-
"""Class that follows VDOM spec, and exposes the user API that can create VDOM elements."""
114+
"""Class-based constructor for VDOM dictionaries.
115+
Once initialized, the `__call__` method on this class is used as the user API
116+
for `reactpy.html`."""
116117

117118
def __init__(
118119
self,
120+
tag_name: str,
119121
/,
120122
allow_children: bool = True,
121123
custom_constructor: CustomVdomConstructor | None = None,
122-
**kwargs: Unpack[VdomTypeDict],
124+
import_source: ImportSourceDict | None = None,
123125
) -> None:
124-
"""`**kwargs` provided here are considered as defaults for dictionary key values.
125-
Other arguments exist to configure the way VDOM dictionaries are constructed."""
126-
if "tagName" not in kwargs:
127-
msg = "You must specify a 'tagName' for a VDOM element."
128-
raise ValueError(msg)
126+
"""Initialize a VDOM constructor for the provided `tag_name`."""
129127
self.allow_children = allow_children
130128
self.custom_constructor = custom_constructor
131-
self.default_values = kwargs
129+
self.import_source = import_source
132130

133131
# Configure Python debugger attributes
134-
self.__name__ = kwargs["tagName"]
132+
self.__name__ = tag_name
135133
module_name = f_module_name(1)
136134
if module_name:
137135
self.__module__ = module_name
138-
self.__qualname__ = f"{module_name}.{kwargs['tagName']}"
136+
self.__qualname__ = f"{module_name}.{tag_name}"
139137

140138
@overload
141139
def __call__(
@@ -163,21 +161,24 @@ def __call__(
163161
attributes=attributes,
164162
event_handlers=event_handlers,
165163
)
164+
166165
# Otherwise, use the default constructor
167166
else:
168167
result = {
169168
**({"key": key} if key is not None else {}),
170169
**({"children": children} if children else {}),
171170
**({"attributes": attributes} if attributes else {}),
172171
**({"eventHandlers": event_handlers} if event_handlers else {}),
172+
**({"importSource": self.import_source} if self.import_source else {}),
173173
}
174174

175175
# Validate the result
176+
result = result | {"tagName": self.__name__}
176177
if children and not self.allow_children:
177-
msg = f"{self.default_values.get('tagName')!r} nodes cannot have children."
178+
msg = f"{self.__name__!r} nodes cannot have children."
178179
raise TypeError(msg)
179180

180-
return VdomDict(**(self.default_values | result)) # type: ignore
181+
return VdomDict(**result) # type: ignore
181182

182183

183184
def separate_attributes_and_children(

‎src/reactpy/web/module.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,9 @@ def _make_export(
284284
allow_children: bool,
285285
) -> VdomConstructor:
286286
return Vdom(
287-
tagName=name,
287+
name,
288288
allow_children=allow_children,
289-
importSource=ImportSourceDict(
289+
import_source=ImportSourceDict(
290290
source=web_module.source,
291291
sourceType=web_module.source_type,
292292
fallback=(fallback or web_module.default_fallback),

‎tests/test_core/test_component.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def SimpleDiv():
2727
async def test_simple_parameterized_component():
2828
@reactpy.component
2929
def SimpleParamComponent(tag):
30-
return reactpy.Vdom(tagName=tag)()
30+
return reactpy.Vdom(tag)()
3131

3232
assert SimpleParamComponent("div").render() == {"tagName": "div"}
3333

‎tests/test_core/test_layout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ async def test_simple_layout():
8282
@reactpy.component
8383
def SimpleComponent():
8484
tag, set_state_hook.current = reactpy.hooks.use_state("div")
85-
return reactpy.Vdom(tagName=tag)()
85+
return reactpy.Vdom(tag)()
8686

8787
async with reactpy.Layout(SimpleComponent()) as layout:
8888
update_1 = await layout.render()

‎tests/test_core/test_vdom.py

Lines changed: 18 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,19 @@ def test_is_vdom(result, value):
3232
"actual, expected",
3333
[
3434
(
35-
reactpy.Vdom(tagName="div")([reactpy.Vdom(tagName="div")()]),
35+
reactpy.Vdom("div")([reactpy.Vdom("div")()]),
3636
{"tagName": "div", "children": [{"tagName": "div"}]},
3737
),
3838
(
39-
reactpy.Vdom(tagName="div")({"style": {"backgroundColor": "red"}}),
39+
reactpy.Vdom("div")({"style": {"backgroundColor": "red"}}),
4040
{"tagName": "div", "attributes": {"style": {"backgroundColor": "red"}}},
4141
),
4242
(
4343
# multiple iterables of children are merged
44-
reactpy.Vdom(tagName="div")(
44+
reactpy.Vdom("div")(
4545
(
46-
[reactpy.Vdom(tagName="div")(), 1],
47-
(reactpy.Vdom(tagName="div")(), 2),
46+
[reactpy.Vdom("div")(), 1],
47+
(reactpy.Vdom("div")(), 2),
4848
)
4949
),
5050
{
@@ -53,13 +53,11 @@ def test_is_vdom(result, value):
5353
},
5454
),
5555
(
56-
reactpy.Vdom(tagName="div")({"onEvent": FAKE_EVENT_HANDLER}),
56+
reactpy.Vdom("div")({"onEvent": FAKE_EVENT_HANDLER}),
5757
{"tagName": "div", "eventHandlers": FAKE_EVENT_HANDLER_DICT},
5858
),
5959
(
60-
reactpy.Vdom(
61-
tagName="div",
62-
)((reactpy.html.h1("hello"), reactpy.html.h2("world"))),
60+
reactpy.Vdom("div")((reactpy.html.h1("hello"), reactpy.html.h2("world"))),
6361
{
6462
"tagName": "div",
6563
"children": [
@@ -69,19 +67,19 @@ def test_is_vdom(result, value):
6967
},
7068
),
7169
(
72-
reactpy.Vdom(tagName="div")({"tagName": "div"}),
70+
reactpy.Vdom("div")({"tagName": "div"}),
7371
{"tagName": "div", "attributes": {"tagName": "div"}},
7472
),
7573
(
76-
reactpy.Vdom(tagName="div")((i for i in range(3))),
74+
reactpy.Vdom("div")((i for i in range(3))),
7775
{"tagName": "div", "children": [0, 1, 2]},
7876
),
7977
(
80-
reactpy.Vdom(tagName="div")((x**2 for x in [1, 2, 3])),
78+
reactpy.Vdom("div")((x**2 for x in [1, 2, 3])),
8179
{"tagName": "div", "children": [1, 4, 9]},
8280
),
8381
(
84-
reactpy.Vdom(tagName="div")(["child_1", ["child_2"]]),
82+
reactpy.Vdom("div")(["child_1", ["child_2"]]),
8583
{"tagName": "div", "children": ["child_1", "child_2"]},
8684
),
8785
],
@@ -93,7 +91,7 @@ def test_simple_node_construction(actual, expected):
9391
async def test_callable_attributes_are_cast_to_event_handlers():
9492
params_from_calls = []
9593

96-
node = reactpy.Vdom(tagName="div")(
94+
node = reactpy.Vdom("div")(
9795
{"onEvent": lambda *args: params_from_calls.append(args)}
9896
)
9997

@@ -109,15 +107,15 @@ async def test_callable_attributes_are_cast_to_event_handlers():
109107

110108

111109
def test_make_vdom_constructor():
112-
elmt = Vdom(tagName="some-tag")
110+
elmt = Vdom("some-tag")
113111

114112
assert elmt({"data": 1}, [elmt()]) == {
115113
"tagName": "some-tag",
116114
"children": [{"tagName": "some-tag"}],
117115
"attributes": {"data": 1},
118116
}
119117

120-
no_children = Vdom(tagName="no-children", allow_children=False)
118+
no_children = Vdom("no-children", allow_children=False)
121119

122120
with pytest.raises(TypeError, match="cannot have children"):
123121
no_children([1, 2, 3])
@@ -295,7 +293,7 @@ def test_invalid_vdom(value, error_message_pattern):
295293
@pytest.mark.skipif(not REACTPY_DEBUG.current, reason="Only warns in debug mode")
296294
def test_warn_cannot_verify_keypath_for_genereators():
297295
with pytest.warns(UserWarning) as record:
298-
reactpy.Vdom(tagName="div")((1 for i in range(10)))
296+
reactpy.Vdom("div")((1 for i in range(10)))
299297
assert len(record) == 1
300298
assert (
301299
record[0]
@@ -307,16 +305,16 @@ def test_warn_cannot_verify_keypath_for_genereators():
307305
@pytest.mark.skipif(not REACTPY_DEBUG.current, reason="Only warns in debug mode")
308306
def test_warn_dynamic_children_must_have_keys():
309307
with pytest.warns(UserWarning) as record:
310-
reactpy.Vdom(tagName="div")([reactpy.Vdom(tagName="div")()])
308+
reactpy.Vdom("div")([reactpy.Vdom("div")()])
311309
assert len(record) == 1
312310
assert record[0].message.args[0].startswith("Key not specified for child")
313311

314312
@reactpy.component
315313
def MyComponent():
316-
return reactpy.Vdom(tagName="div")()
314+
return reactpy.Vdom("div")()
317315

318316
with pytest.warns(UserWarning) as record:
319-
reactpy.Vdom(tagName="div")([MyComponent()])
317+
reactpy.Vdom("div")([MyComponent()])
320318
assert len(record) == 1
321319
assert record[0].message.args[0].startswith("Key not specified for child")
322320

@@ -328,12 +326,6 @@ def test_raise_for_non_json_attrs():
328326

329327

330328
def test_invalid_vdom_keys():
331-
with pytest.raises(ValueError, match="Invalid keys*"):
332-
reactpy.Vdom(tagName="div", foo="bar")()
333-
334-
with pytest.raises(ValueError, match="You must specify a 'tagName'*"):
335-
reactpy.Vdom()
336-
337329
with pytest.raises(ValueError, match="Invalid keys:*"):
338330
reactpy.types.VdomDict(tagName="test", foo="bar")
339331

0 commit comments

Comments
 (0)
Please sign in to comment.