reactpy 2.0.0b4__py3-none-any.whl → 2.0.0b6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reactpy/__init__.py +3 -2
- reactpy/_console/rewrite_props.py +2 -2
- reactpy/_html.py +11 -9
- reactpy/_option.py +2 -1
- reactpy/config.py +2 -2
- reactpy/core/_life_cycle_hook.py +12 -10
- reactpy/core/_thread_local.py +2 -1
- reactpy/core/component.py +4 -38
- reactpy/core/events.py +61 -36
- reactpy/core/hooks.py +25 -35
- reactpy/core/layout.py +193 -201
- reactpy/core/serve.py +17 -22
- reactpy/core/vdom.py +9 -12
- reactpy/executors/asgi/__init__.py +9 -4
- reactpy/executors/asgi/middleware.py +1 -2
- reactpy/executors/asgi/pyscript.py +3 -7
- reactpy/executors/asgi/standalone.py +4 -6
- reactpy/executors/asgi/types.py +2 -2
- reactpy/pyscript/components.py +3 -3
- reactpy/pyscript/utils.py +49 -46
- reactpy/reactjs/__init__.py +353 -0
- reactpy/reactjs/module.py +203 -0
- reactpy/reactjs/types.py +7 -0
- reactpy/reactjs/utils.py +183 -0
- reactpy/static/index-h31022cd.js +5 -0
- reactpy/static/index-h31022cd.js.map +11 -0
- reactpy/static/index-sbddj6ms.js +5 -0
- reactpy/static/index-sbddj6ms.js.map +10 -0
- reactpy/static/index-y71bxs88.js +5 -0
- reactpy/static/index-y71bxs88.js.map +10 -0
- reactpy/static/index.js +2 -2
- reactpy/static/index.js.map +6 -10
- reactpy/static/react-dom.js +4 -0
- reactpy/static/react-dom.js.map +11 -0
- reactpy/static/react-jsx-runtime.js +4 -0
- reactpy/static/react-jsx-runtime.js.map +9 -0
- reactpy/static/react.js +4 -0
- reactpy/static/react.js.map +10 -0
- reactpy/testing/backend.py +6 -5
- reactpy/testing/common.py +3 -5
- reactpy/testing/display.py +2 -1
- reactpy/testing/logs.py +1 -1
- reactpy/transforms.py +2 -2
- reactpy/types.py +117 -58
- reactpy/utils.py +8 -8
- reactpy/web/__init__.py +0 -6
- reactpy/web/module.py +37 -470
- reactpy/web/utils.py +2 -158
- reactpy/widgets.py +2 -2
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/METADATA +4 -7
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/RECORD +54 -39
- reactpy/web/templates/react.js +0 -61
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/WHEEL +0 -0
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/entry_points.txt +0 -0
- {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/licenses/LICENSE +0 -0
reactpy/core/vdom.py
CHANGED
|
@@ -3,10 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
import re
|
|
6
|
-
from collections.abc import Mapping, Sequence
|
|
6
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
7
7
|
from typing import (
|
|
8
8
|
Any,
|
|
9
|
-
Callable,
|
|
10
9
|
cast,
|
|
11
10
|
overload,
|
|
12
11
|
)
|
|
@@ -18,11 +17,11 @@ from reactpy.config import REACTPY_CHECK_JSON_ATTRS, REACTPY_DEBUG
|
|
|
18
17
|
from reactpy.core._f_back import f_module_name
|
|
19
18
|
from reactpy.core.events import EventHandler, to_event_handler_function
|
|
20
19
|
from reactpy.types import (
|
|
21
|
-
|
|
20
|
+
BaseEventHandler,
|
|
21
|
+
Component,
|
|
22
22
|
CustomVdomConstructor,
|
|
23
23
|
EllipsisRepr,
|
|
24
24
|
EventHandlerDict,
|
|
25
|
-
EventHandlerType,
|
|
26
25
|
ImportSourceDict,
|
|
27
26
|
InlineJavaScript,
|
|
28
27
|
InlineJavaScriptDict,
|
|
@@ -42,7 +41,6 @@ VDOM_JSON_SCHEMA = {
|
|
|
42
41
|
"type": "object",
|
|
43
42
|
"properties": {
|
|
44
43
|
"tagName": {"type": "string"},
|
|
45
|
-
"key": {"type": ["string", "number", "null"]},
|
|
46
44
|
"error": {"type": "string"},
|
|
47
45
|
"children": {"$ref": "#/definitions/elementChildren"},
|
|
48
46
|
"attributes": {"type": "object"},
|
|
@@ -171,7 +169,6 @@ class Vdom:
|
|
|
171
169
|
) -> VdomDict:
|
|
172
170
|
"""The entry point for the VDOM API, for example reactpy.html(<WE_ARE_HERE>)."""
|
|
173
171
|
attributes, children = separate_attributes_and_children(attributes_and_children)
|
|
174
|
-
key = attributes.get("key", None)
|
|
175
172
|
attributes, event_handlers, inline_javascript = (
|
|
176
173
|
separate_attributes_handlers_and_inline_javascript(attributes)
|
|
177
174
|
)
|
|
@@ -181,7 +178,6 @@ class Vdom:
|
|
|
181
178
|
# Run custom constructor, if defined
|
|
182
179
|
if self.custom_constructor:
|
|
183
180
|
result = self.custom_constructor(
|
|
184
|
-
key=key,
|
|
185
181
|
children=children,
|
|
186
182
|
attributes=attributes,
|
|
187
183
|
event_handlers=event_handlers,
|
|
@@ -190,7 +186,6 @@ class Vdom:
|
|
|
190
186
|
# Otherwise, use the default constructor
|
|
191
187
|
else:
|
|
192
188
|
result = {
|
|
193
|
-
**({"key": key} if key is not None else {}),
|
|
194
189
|
**({"children": children} if children else {}),
|
|
195
190
|
**({"attributes": attributes} if attributes else {}),
|
|
196
191
|
**({"eventHandlers": event_handlers} if event_handlers else {}),
|
|
@@ -232,13 +227,13 @@ def separate_attributes_handlers_and_inline_javascript(
|
|
|
232
227
|
attributes: Mapping[str, Any],
|
|
233
228
|
) -> tuple[VdomAttributes, EventHandlerDict, InlineJavaScriptDict]:
|
|
234
229
|
_attributes: VdomAttributes = {}
|
|
235
|
-
_event_handlers: dict[str,
|
|
230
|
+
_event_handlers: dict[str, BaseEventHandler] = {}
|
|
236
231
|
_inline_javascript: dict[str, InlineJavaScript] = {}
|
|
237
232
|
|
|
238
233
|
for k, v in attributes.items():
|
|
239
234
|
if callable(v):
|
|
240
235
|
_event_handlers[k] = EventHandler(to_event_handler_function(v))
|
|
241
|
-
elif isinstance(v,
|
|
236
|
+
elif isinstance(v, BaseEventHandler):
|
|
242
237
|
_event_handlers[k] = v
|
|
243
238
|
elif EVENT_ATTRIBUTE_PATTERN.match(k) and isinstance(v, str):
|
|
244
239
|
_inline_javascript[k] = InlineJavaScript(v)
|
|
@@ -276,9 +271,11 @@ def _validate_child_key_integrity(value: Any) -> None:
|
|
|
276
271
|
)
|
|
277
272
|
else:
|
|
278
273
|
for child in value:
|
|
279
|
-
if isinstance(child,
|
|
274
|
+
if isinstance(child, Component) and child.key is None:
|
|
280
275
|
warn(f"Key not specified for child in list {child}", UserWarning)
|
|
281
|
-
elif isinstance(child, Mapping) and "key" not in child
|
|
276
|
+
elif isinstance(child, Mapping) and "key" not in child.get(
|
|
277
|
+
"attributes", {}
|
|
278
|
+
):
|
|
282
279
|
# remove 'children' to reduce log spam
|
|
283
280
|
child_copy = {**child, "children": EllipsisRepr()}
|
|
284
281
|
warn(f"Key not specified for child in list {child_copy}", UserWarning)
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
from reactpy.executors.asgi.
|
|
3
|
-
from reactpy.executors.asgi.
|
|
1
|
+
try:
|
|
2
|
+
from reactpy.executors.asgi.middleware import ReactPyMiddleware
|
|
3
|
+
from reactpy.executors.asgi.pyscript import ReactPyCsr
|
|
4
|
+
from reactpy.executors.asgi.standalone import ReactPy
|
|
4
5
|
|
|
5
|
-
__all__ = ["ReactPy", "ReactPyCsr", "ReactPyMiddleware"]
|
|
6
|
+
__all__ = ["ReactPy", "ReactPyCsr", "ReactPyMiddleware"]
|
|
7
|
+
except ModuleNotFoundError as e:
|
|
8
|
+
raise ModuleNotFoundError(
|
|
9
|
+
"ASGI executors require the 'reactpy[asgi]' extra to be installed."
|
|
10
|
+
) from e
|
|
@@ -8,13 +8,12 @@ import urllib.parse
|
|
|
8
8
|
from collections.abc import Iterable
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Any
|
|
11
|
+
from typing import Any, Unpack
|
|
12
12
|
|
|
13
13
|
import orjson
|
|
14
14
|
from asgi_tools import ResponseText, ResponseWebSocket
|
|
15
15
|
from asgiref.compatibility import guarantee_single_callable
|
|
16
16
|
from servestatic import ServeStaticASGI
|
|
17
|
-
from typing_extensions import Unpack
|
|
18
17
|
|
|
19
18
|
from reactpy import config
|
|
20
19
|
from reactpy.core.hooks import ConnectionContext
|
|
@@ -4,12 +4,10 @@ import hashlib
|
|
|
4
4
|
import re
|
|
5
5
|
from collections.abc import Sequence
|
|
6
6
|
from dataclasses import dataclass
|
|
7
|
-
from datetime import
|
|
7
|
+
from datetime import UTC, datetime
|
|
8
8
|
from email.utils import formatdate
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import Any
|
|
11
|
-
|
|
12
|
-
from typing_extensions import Unpack
|
|
10
|
+
from typing import Any, Unpack
|
|
13
11
|
|
|
14
12
|
from reactpy import html
|
|
15
13
|
from reactpy.executors.asgi.middleware import ReactPyMiddleware
|
|
@@ -118,6 +116,4 @@ class ReactPyPyscriptApp(ReactPyApp):
|
|
|
118
116
|
"</html>"
|
|
119
117
|
)
|
|
120
118
|
self._etag = f'"{hashlib.md5(self._index_html.encode(), usedforsecurity=False).hexdigest()}"'
|
|
121
|
-
self._last_modified = formatdate(
|
|
122
|
-
datetime.now(tz=timezone.utc).timestamp(), usegmt=True
|
|
123
|
-
)
|
|
119
|
+
self._last_modified = formatdate(datetime.now(tz=UTC).timestamp(), usegmt=True)
|
|
@@ -2,14 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import hashlib
|
|
4
4
|
import re
|
|
5
|
+
from collections.abc import Callable
|
|
5
6
|
from dataclasses import dataclass
|
|
6
|
-
from datetime import
|
|
7
|
+
from datetime import UTC, datetime
|
|
7
8
|
from email.utils import formatdate
|
|
8
9
|
from logging import getLogger
|
|
9
|
-
from typing import
|
|
10
|
+
from typing import Literal, Unpack, cast, overload
|
|
10
11
|
|
|
11
12
|
from asgi_tools import ResponseHTML
|
|
12
|
-
from typing_extensions import Unpack
|
|
13
13
|
|
|
14
14
|
from reactpy import html
|
|
15
15
|
from reactpy.executors.asgi.middleware import ReactPyMiddleware
|
|
@@ -239,6 +239,4 @@ class ReactPyApp:
|
|
|
239
239
|
"</html>"
|
|
240
240
|
)
|
|
241
241
|
self._etag = f'"{hashlib.md5(self._index_html.encode(), usedforsecurity=False).hexdigest()}"'
|
|
242
|
-
self._last_modified = formatdate(
|
|
243
|
-
datetime.now(tz=timezone.utc).timestamp(), usegmt=True
|
|
244
|
-
)
|
|
242
|
+
self._last_modified = formatdate(datetime.now(tz=UTC).timestamp(), usegmt=True)
|
reactpy/executors/asgi/types.py
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from collections.abc import Awaitable, MutableMapping
|
|
6
|
-
from typing import Any,
|
|
5
|
+
from collections.abc import Awaitable, Callable, MutableMapping
|
|
6
|
+
from typing import Any, Protocol
|
|
7
7
|
|
|
8
8
|
from asgiref import typing as asgi_types
|
|
9
9
|
|
reactpy/pyscript/components.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
|
|
|
5
5
|
|
|
6
6
|
from reactpy import component, hooks
|
|
7
7
|
from reactpy.pyscript.utils import pyscript_component_html
|
|
8
|
-
from reactpy.types import
|
|
8
|
+
from reactpy.types import Component, Key
|
|
9
9
|
from reactpy.utils import string_to_reactpy
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
@@ -39,10 +39,10 @@ def _pyscript_component(
|
|
|
39
39
|
|
|
40
40
|
def pyscript_component(
|
|
41
41
|
*file_paths: str | Path,
|
|
42
|
-
initial: str | VdomDict |
|
|
42
|
+
initial: str | VdomDict | Component = "",
|
|
43
43
|
root: str = "root",
|
|
44
44
|
key: Key | None = None,
|
|
45
|
-
) ->
|
|
45
|
+
) -> Component:
|
|
46
46
|
"""
|
|
47
47
|
Args:
|
|
48
48
|
file_paths: File path to your client-side ReactPy component. If multiple paths are \
|
reactpy/pyscript/utils.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ruff: noqa:
|
|
1
|
+
# ruff: noqa: S607
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import functools
|
|
@@ -11,6 +11,7 @@ from glob import glob
|
|
|
11
11
|
from logging import getLogger
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import TYPE_CHECKING, Any
|
|
14
|
+
from urllib import request
|
|
14
15
|
from uuid import uuid4
|
|
15
16
|
|
|
16
17
|
import reactpy
|
|
@@ -110,8 +111,6 @@ def extend_pyscript_config(
|
|
|
110
111
|
extra_js: dict[str, str] | str,
|
|
111
112
|
config: dict[str, Any] | str,
|
|
112
113
|
) -> str:
|
|
113
|
-
import orjson
|
|
114
|
-
|
|
115
114
|
# Extends ReactPy's default PyScript config with user provided values.
|
|
116
115
|
pyscript_config: dict[str, Any] = {
|
|
117
116
|
"packages": [reactpy_version_string(), "jsonpointer==3.*", "ssl"],
|
|
@@ -120,10 +119,13 @@ def extend_pyscript_config(
|
|
|
120
119
|
f"{REACTPY_PATH_PREFIX.current}static/morphdom/morphdom-esm.js": "morphdom"
|
|
121
120
|
}
|
|
122
121
|
},
|
|
123
|
-
"packages_cache": "never",
|
|
124
122
|
}
|
|
125
123
|
pyscript_config["packages"].extend(extra_py)
|
|
126
124
|
|
|
125
|
+
# FIXME: https://github.com/pyscript/pyscript/issues/2282
|
|
126
|
+
if any(pkg.endswith(".whl") for pkg in pyscript_config["packages"]): # nocov
|
|
127
|
+
pyscript_config["packages_cache"] = "never"
|
|
128
|
+
|
|
127
129
|
# Extend the JavaScript dependency list
|
|
128
130
|
if extra_js and isinstance(extra_js, str):
|
|
129
131
|
pyscript_config["js_modules"]["main"].update(json.loads(extra_js))
|
|
@@ -135,7 +137,7 @@ def extend_pyscript_config(
|
|
|
135
137
|
pyscript_config.update(json.loads(config))
|
|
136
138
|
elif config and isinstance(config, dict):
|
|
137
139
|
pyscript_config.update(config)
|
|
138
|
-
return
|
|
140
|
+
return json.dumps(pyscript_config)
|
|
139
141
|
|
|
140
142
|
|
|
141
143
|
def reactpy_version_string() -> str: # nocov
|
|
@@ -144,10 +146,10 @@ def reactpy_version_string() -> str: # nocov
|
|
|
144
146
|
local_version = reactpy.__version__
|
|
145
147
|
|
|
146
148
|
# Get a list of all versions via `pip index versions`
|
|
147
|
-
result =
|
|
149
|
+
result = get_reactpy_versions()
|
|
148
150
|
|
|
149
151
|
# Check if the command failed
|
|
150
|
-
if result
|
|
152
|
+
if not result:
|
|
151
153
|
_logger.warning(
|
|
152
154
|
"Failed to verify what versions of ReactPy exist on PyPi. "
|
|
153
155
|
"PyScript functionality may not work as expected.",
|
|
@@ -155,16 +157,8 @@ def reactpy_version_string() -> str: # nocov
|
|
|
155
157
|
return f"reactpy=={local_version}"
|
|
156
158
|
|
|
157
159
|
# Have `pip` tell us what versions are available
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
known_versions: list[str] = []
|
|
161
|
-
latest_version: str = ""
|
|
162
|
-
for line in result.stdout.splitlines():
|
|
163
|
-
if line.startswith(available_version_symbol):
|
|
164
|
-
known_versions.extend(line[len(available_version_symbol) :].split(", "))
|
|
165
|
-
elif latest_version_symbol in line:
|
|
166
|
-
symbol_postion = line.index(latest_version_symbol)
|
|
167
|
-
latest_version = line[symbol_postion + len(latest_version_symbol) :].strip()
|
|
160
|
+
known_versions: list[str] = result.get("versions", [])
|
|
161
|
+
latest_version: str = result.get("latest", "")
|
|
168
162
|
|
|
169
163
|
# Return early if the version is available on PyPi and we're not in a CI environment
|
|
170
164
|
if local_version in known_versions and not GITHUB_ACTIONS:
|
|
@@ -173,8 +167,8 @@ def reactpy_version_string() -> str: # nocov
|
|
|
173
167
|
# We are now determining an alternative method of installing ReactPy for PyScript
|
|
174
168
|
if not GITHUB_ACTIONS:
|
|
175
169
|
_logger.warning(
|
|
176
|
-
"Your
|
|
177
|
-
"
|
|
170
|
+
"Your ReactPy version isn't available on PyPi. "
|
|
171
|
+
"Attempting to find an alternative installation method for PyScript...",
|
|
178
172
|
)
|
|
179
173
|
|
|
180
174
|
# Build a local wheel for ReactPy, if needed
|
|
@@ -189,42 +183,51 @@ def reactpy_version_string() -> str: # nocov
|
|
|
189
183
|
check=False,
|
|
190
184
|
cwd=Path(reactpy.__file__).parent.parent.parent,
|
|
191
185
|
)
|
|
192
|
-
|
|
186
|
+
wheel_glob = glob(str(dist_dir / f"reactpy-{local_version}-*.whl"))
|
|
193
187
|
|
|
194
|
-
#
|
|
195
|
-
if
|
|
196
|
-
|
|
188
|
+
# Move the local wheel to the web modules directory, if it exists
|
|
189
|
+
if wheel_glob:
|
|
190
|
+
wheel_file = Path(wheel_glob[0])
|
|
191
|
+
new_path = REACTPY_WEB_MODULES_DIR.current / wheel_file.name
|
|
192
|
+
if not new_path.exists():
|
|
197
193
|
_logger.warning(
|
|
198
|
-
"
|
|
199
|
-
|
|
194
|
+
"PyScript will utilize local wheel '%s'.",
|
|
195
|
+
wheel_file.name,
|
|
200
196
|
)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"Failed to build a local wheel for ReactPy, and could not determine the latest version on PyPi. "
|
|
204
|
-
"PyScript functionality may not work as expected.",
|
|
205
|
-
)
|
|
206
|
-
return f"reactpy=={local_version}"
|
|
197
|
+
shutil.copy(wheel_file, new_path)
|
|
198
|
+
return f"{REACTPY_PATH_PREFIX.current}modules/{wheel_file.name}"
|
|
207
199
|
|
|
208
|
-
#
|
|
209
|
-
|
|
210
|
-
new_path = REACTPY_WEB_MODULES_DIR.current / wheel_file.name
|
|
211
|
-
if not new_path.exists():
|
|
200
|
+
# Building a local wheel failed, try our best to give the user any version.
|
|
201
|
+
if latest_version:
|
|
212
202
|
_logger.warning(
|
|
213
|
-
"
|
|
214
|
-
|
|
203
|
+
"Failed to build a local wheel for ReactPy, likely due to missing build dependencies. "
|
|
204
|
+
"PyScript will default to using the latest ReactPy version on PyPi."
|
|
215
205
|
)
|
|
216
|
-
|
|
217
|
-
|
|
206
|
+
return f"reactpy=={latest_version}"
|
|
207
|
+
_logger.error(
|
|
208
|
+
"Failed to build a local wheel for ReactPy, and could not determine the latest version on PyPi. "
|
|
209
|
+
"PyScript functionality may not work as expected.",
|
|
210
|
+
)
|
|
211
|
+
return f"reactpy=={local_version}"
|
|
218
212
|
|
|
219
213
|
|
|
220
214
|
@functools.cache
|
|
221
|
-
def
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
215
|
+
def get_reactpy_versions() -> dict[Any, Any]:
|
|
216
|
+
"""Fetches the available versions of a package from PyPI."""
|
|
217
|
+
try:
|
|
218
|
+
try:
|
|
219
|
+
response = request.urlopen("https://pypi.org/pypi/reactpy/json", timeout=5)
|
|
220
|
+
except Exception:
|
|
221
|
+
response = request.urlopen("http://pypi.org/pypi/reactpy/json", timeout=5)
|
|
222
|
+
if response.status == 200: # noqa: PLR2004
|
|
223
|
+
data = json.load(response)
|
|
224
|
+
versions = list(data.get("releases", {}).keys())
|
|
225
|
+
latest = data.get("info", {}).get("version", "")
|
|
226
|
+
if versions and latest:
|
|
227
|
+
return {"versions": versions, "latest": latest}
|
|
228
|
+
except Exception:
|
|
229
|
+
_logger.exception("Error fetching ReactPy package versions from PyPI!")
|
|
230
|
+
return {}
|
|
228
231
|
|
|
229
232
|
|
|
230
233
|
@functools.cache
|