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.
Files changed (55) hide show
  1. reactpy/__init__.py +3 -2
  2. reactpy/_console/rewrite_props.py +2 -2
  3. reactpy/_html.py +11 -9
  4. reactpy/_option.py +2 -1
  5. reactpy/config.py +2 -2
  6. reactpy/core/_life_cycle_hook.py +12 -10
  7. reactpy/core/_thread_local.py +2 -1
  8. reactpy/core/component.py +4 -38
  9. reactpy/core/events.py +61 -36
  10. reactpy/core/hooks.py +25 -35
  11. reactpy/core/layout.py +193 -201
  12. reactpy/core/serve.py +17 -22
  13. reactpy/core/vdom.py +9 -12
  14. reactpy/executors/asgi/__init__.py +9 -4
  15. reactpy/executors/asgi/middleware.py +1 -2
  16. reactpy/executors/asgi/pyscript.py +3 -7
  17. reactpy/executors/asgi/standalone.py +4 -6
  18. reactpy/executors/asgi/types.py +2 -2
  19. reactpy/pyscript/components.py +3 -3
  20. reactpy/pyscript/utils.py +49 -46
  21. reactpy/reactjs/__init__.py +353 -0
  22. reactpy/reactjs/module.py +203 -0
  23. reactpy/reactjs/types.py +7 -0
  24. reactpy/reactjs/utils.py +183 -0
  25. reactpy/static/index-h31022cd.js +5 -0
  26. reactpy/static/index-h31022cd.js.map +11 -0
  27. reactpy/static/index-sbddj6ms.js +5 -0
  28. reactpy/static/index-sbddj6ms.js.map +10 -0
  29. reactpy/static/index-y71bxs88.js +5 -0
  30. reactpy/static/index-y71bxs88.js.map +10 -0
  31. reactpy/static/index.js +2 -2
  32. reactpy/static/index.js.map +6 -10
  33. reactpy/static/react-dom.js +4 -0
  34. reactpy/static/react-dom.js.map +11 -0
  35. reactpy/static/react-jsx-runtime.js +4 -0
  36. reactpy/static/react-jsx-runtime.js.map +9 -0
  37. reactpy/static/react.js +4 -0
  38. reactpy/static/react.js.map +10 -0
  39. reactpy/testing/backend.py +6 -5
  40. reactpy/testing/common.py +3 -5
  41. reactpy/testing/display.py +2 -1
  42. reactpy/testing/logs.py +1 -1
  43. reactpy/transforms.py +2 -2
  44. reactpy/types.py +117 -58
  45. reactpy/utils.py +8 -8
  46. reactpy/web/__init__.py +0 -6
  47. reactpy/web/module.py +37 -470
  48. reactpy/web/utils.py +2 -158
  49. reactpy/widgets.py +2 -2
  50. {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/METADATA +4 -7
  51. {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/RECORD +54 -39
  52. reactpy/web/templates/react.js +0 -61
  53. {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/WHEEL +0 -0
  54. {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/entry_points.txt +0 -0
  55. {reactpy-2.0.0b4.dist-info → reactpy-2.0.0b6.dist-info}/licenses/LICENSE +0 -0
reactpy/web/module.py CHANGED
@@ -1,281 +1,44 @@
1
1
  from __future__ import annotations
2
2
 
3
- import filecmp
4
- import logging
5
- import shutil
6
- from dataclasses import dataclass
7
3
  from pathlib import Path
8
- from typing import Any, NewType, overload
4
+ from typing import Any, overload
9
5
 
10
6
  from reactpy._warnings import warn
11
- from reactpy.config import REACTPY_DEBUG, REACTPY_WEB_MODULES_DIR
12
- from reactpy.core.vdom import Vdom
13
- from reactpy.types import ImportSourceDict, VdomConstructor
14
- from reactpy.web.utils import (
15
- module_name_suffix,
16
- resolve_module_exports_from_file,
17
- resolve_module_exports_from_url,
7
+ from reactpy.reactjs.types import (
8
+ NAME_SOURCE,
9
+ URL_SOURCE,
10
+ SourceType,
18
11
  )
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
- SourceType = NewType("SourceType", str)
23
-
24
- NAME_SOURCE = SourceType("NAME")
25
- """A named source - usually a Javascript package name"""
26
-
27
- URL_SOURCE = SourceType("URL")
28
- """A source loaded from a URL, usually a CDN"""
29
-
30
-
31
- _URL_WEB_MODULE_CACHE: dict[str, WebModule] = {}
32
- _FILE_WEB_MODULE_CACHE: dict[str, WebModule] = {}
33
- _STRING_WEB_MODULE_CACHE: dict[str, WebModule] = {}
34
-
35
-
36
- @overload
37
- def reactjs_component_from_url(
38
- url: str,
39
- import_names: str,
40
- fallback: Any | None = ...,
41
- resolve_imports: bool | None = ...,
42
- resolve_imports_depth: int = ...,
43
- unmount_before_update: bool = ...,
44
- allow_children: bool = ...,
45
- ) -> VdomConstructor: ...
46
-
47
-
48
- @overload
49
- def reactjs_component_from_url(
50
- url: str,
51
- import_names: list[str] | tuple[str, ...],
52
- fallback: Any | None = ...,
53
- resolve_imports: bool | None = ...,
54
- resolve_imports_depth: int = ...,
55
- unmount_before_update: bool = ...,
56
- allow_children: bool = ...,
57
- ) -> list[VdomConstructor]: ...
58
-
59
-
60
- def reactjs_component_from_url(
61
- url: str,
62
- import_names: str | list[str] | tuple[str, ...],
63
- fallback: Any | None = None,
64
- resolve_imports: bool | None = None,
65
- resolve_imports_depth: int = 5,
66
- unmount_before_update: bool = False,
67
- allow_children: bool = True,
68
- ) -> VdomConstructor | list[VdomConstructor]:
69
- """Import a component from a URL.
70
-
71
- Parameters:
72
- url:
73
- The URL to import the component from.
74
- import_names:
75
- One or more component names to import. If given as a string, a single component
76
- will be returned. If a list is given, then a list of components will be
77
- returned.
78
- fallback:
79
- What to temporarily display while the module is being loaded.
80
- resolve_imports:
81
- Whether to try and find all the named imports of this module.
82
- resolve_imports_depth:
83
- How deeply to search for those imports.
84
- unmount_before_update:
85
- Cause the component to be unmounted before each update. This option should
86
- only be used if the imported package fails to re-render when props change.
87
- Using this option has negative performance consequences since all DOM
88
- elements must be changed on each render. See :issue:`461` for more info.
89
- allow_children:
90
- Whether or not these components can have children.
91
- """
92
- key = f"{url}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
93
- if key in _URL_WEB_MODULE_CACHE:
94
- module = _URL_WEB_MODULE_CACHE[key]
95
- else:
96
- module = _module_from_url(
97
- url,
98
- fallback=fallback,
99
- resolve_imports=resolve_imports,
100
- resolve_imports_depth=resolve_imports_depth,
101
- unmount_before_update=unmount_before_update,
102
- )
103
- _URL_WEB_MODULE_CACHE[key] = module
104
- return _vdom_from_web_module(module, import_names, fallback, allow_children)
105
-
106
-
107
- @overload
108
- def reactjs_component_from_file(
109
- name: str,
110
- file: str | Path,
111
- import_names: str,
112
- fallback: Any | None = ...,
113
- resolve_imports: bool | None = ...,
114
- resolve_imports_depth: int = ...,
115
- unmount_before_update: bool = ...,
116
- symlink: bool = ...,
117
- allow_children: bool = ...,
118
- ) -> VdomConstructor: ...
119
-
120
-
121
- @overload
122
- def reactjs_component_from_file(
123
- name: str,
124
- file: str | Path,
125
- import_names: list[str] | tuple[str, ...],
126
- fallback: Any | None = ...,
127
- resolve_imports: bool | None = ...,
128
- resolve_imports_depth: int = ...,
129
- unmount_before_update: bool = ...,
130
- symlink: bool = ...,
131
- allow_children: bool = ...,
132
- ) -> list[VdomConstructor]: ...
133
-
134
-
135
- def reactjs_component_from_file(
136
- name: str,
137
- file: str | Path,
138
- import_names: str | list[str] | tuple[str, ...],
139
- fallback: Any | None = None,
140
- resolve_imports: bool | None = None,
141
- resolve_imports_depth: int = 5,
142
- unmount_before_update: bool = False,
143
- symlink: bool = False,
144
- allow_children: bool = True,
145
- ) -> VdomConstructor | list[VdomConstructor]:
146
- """Import a component from a file.
147
-
148
- Parameters:
149
- name:
150
- The name of the package
151
- file:
152
- The file from which the content of the web module will be created.
153
- import_names:
154
- One or more component names to import. If given as a string, a single component
155
- will be returned. If a list is given, then a list of components will be
156
- returned.
157
- fallback:
158
- What to temporarily display while the module is being loaded.
159
- resolve_imports:
160
- Whether to try and find all the named imports of this module.
161
- resolve_imports_depth:
162
- How deeply to search for those imports.
163
- unmount_before_update:
164
- Cause the component to be unmounted before each update. This option should
165
- only be used if the imported package fails to re-render when props change.
166
- Using this option has negative performance consequences since all DOM
167
- elements must be changed on each render. See :issue:`461` for more info.
168
- symlink:
169
- Whether the web module should be saved as a symlink to the given ``file``.
170
- allow_children:
171
- Whether or not these components can have children.
172
- """
173
- key = f"{name}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
174
- if key in _FILE_WEB_MODULE_CACHE:
175
- module = _FILE_WEB_MODULE_CACHE[key]
176
- else:
177
- module = _module_from_file(
178
- name,
179
- file,
180
- fallback=fallback,
181
- resolve_imports=resolve_imports,
182
- resolve_imports_depth=resolve_imports_depth,
183
- unmount_before_update=unmount_before_update,
184
- symlink=symlink,
185
- )
186
- _FILE_WEB_MODULE_CACHE[key] = module
187
- return _vdom_from_web_module(module, import_names, fallback, allow_children)
188
-
189
-
190
- @overload
191
- def reactjs_component_from_string(
192
- name: str,
193
- content: str,
194
- import_names: str,
195
- fallback: Any | None = ...,
196
- resolve_imports: bool | None = ...,
197
- resolve_imports_depth: int = ...,
198
- unmount_before_update: bool = ...,
199
- allow_children: bool = ...,
200
- ) -> VdomConstructor: ...
201
-
202
-
203
- @overload
204
- def reactjs_component_from_string(
205
- name: str,
206
- content: str,
207
- import_names: list[str] | tuple[str, ...],
208
- fallback: Any | None = ...,
209
- resolve_imports: bool | None = ...,
210
- resolve_imports_depth: int = ...,
211
- unmount_before_update: bool = ...,
212
- allow_children: bool = ...,
213
- ) -> list[VdomConstructor]: ...
214
-
215
-
216
- def reactjs_component_from_string(
217
- name: str,
218
- content: str,
219
- import_names: str | list[str] | tuple[str, ...],
220
- fallback: Any | None = None,
221
- resolve_imports: bool | None = None,
222
- resolve_imports_depth: int = 5,
223
- unmount_before_update: bool = False,
224
- allow_children: bool = True,
225
- ) -> VdomConstructor | list[VdomConstructor]:
226
- """Import a component from a string.
227
-
228
- Parameters:
229
- name:
230
- The name of the package
231
- content:
232
- The contents of the web module
233
- import_names:
234
- One or more component names to import. If given as a string, a single component
235
- will be returned. If a list is given, then a list of components will be
236
- returned.
237
- fallback:
238
- What to temporarily display while the module is being loaded.
239
- resolve_imports:
240
- Whether to try and find all the named imports of this module.
241
- resolve_imports_depth:
242
- How deeply to search for those imports.
243
- unmount_before_update:
244
- Cause the component to be unmounted before each update. This option should
245
- only be used if the imported package fails to re-render when props change.
246
- Using this option has negative performance consequences since all DOM
247
- elements must be changed on each render. See :issue:`461` for more info.
248
- allow_children:
249
- Whether or not these components can have children.
250
- """
251
- key = f"{name}{resolve_imports}{resolve_imports_depth}{unmount_before_update}"
252
- if key in _STRING_WEB_MODULE_CACHE:
253
- module = _STRING_WEB_MODULE_CACHE[key]
254
- else:
255
- module = _module_from_string(
256
- name,
257
- content,
258
- fallback=fallback,
259
- resolve_imports=resolve_imports,
260
- resolve_imports_depth=resolve_imports_depth,
261
- unmount_before_update=unmount_before_update,
262
- )
263
- _STRING_WEB_MODULE_CACHE[key] = module
264
- return _vdom_from_web_module(module, import_names, fallback, allow_children)
12
+ from reactpy.types import JavaScriptModule as WebModule
13
+ from reactpy.types import VdomConstructor
14
+
15
+ # Re-export for backward compatibility
16
+ __all__ = [
17
+ "NAME_SOURCE",
18
+ "URL_SOURCE",
19
+ "SourceType",
20
+ "WebModule",
21
+ "export",
22
+ "module_from_file",
23
+ "module_from_string",
24
+ "module_from_url",
25
+ ]
265
26
 
266
27
 
267
28
  def module_from_url(
268
29
  url: str,
269
30
  fallback: Any | None = None,
270
- resolve_exports: bool | None = None,
31
+ resolve_exports: bool = False,
271
32
  resolve_exports_depth: int = 5,
272
33
  unmount_before_update: bool = False,
273
34
  ) -> WebModule: # pragma: no cover
274
35
  warn(
275
- "module_from_url is deprecated, use reactjs_component_from_url instead",
36
+ "module_from_url is deprecated, use component_from_url instead",
276
37
  DeprecationWarning,
277
38
  )
278
- return _module_from_url(
39
+ from reactpy.reactjs.module import url_to_module
40
+
41
+ return url_to_module(
279
42
  url,
280
43
  fallback=fallback,
281
44
  resolve_imports=resolve_exports,
@@ -288,16 +51,18 @@ def module_from_file(
288
51
  name: str,
289
52
  file: str | Path,
290
53
  fallback: Any | None = None,
291
- resolve_exports: bool | None = None,
54
+ resolve_exports: bool = False,
292
55
  resolve_exports_depth: int = 5,
293
56
  unmount_before_update: bool = False,
294
57
  symlink: bool = False,
295
58
  ) -> WebModule: # pragma: no cover
296
59
  warn(
297
- "module_from_file is deprecated, use reactjs_component_from_file instead",
60
+ "module_from_file is deprecated, use component_from_file instead",
298
61
  DeprecationWarning,
299
62
  )
300
- return _module_from_file(
63
+ from reactpy.reactjs.module import file_to_module
64
+
65
+ return file_to_module(
301
66
  name,
302
67
  file,
303
68
  fallback=fallback,
@@ -312,15 +77,17 @@ def module_from_string(
312
77
  name: str,
313
78
  content: str,
314
79
  fallback: Any | None = None,
315
- resolve_exports: bool | None = None,
80
+ resolve_exports: bool = False,
316
81
  resolve_exports_depth: int = 5,
317
82
  unmount_before_update: bool = False,
318
83
  ) -> WebModule: # pragma: no cover
319
84
  warn(
320
- "module_from_string is deprecated, use reactjs_component_from_string instead",
85
+ "module_from_string is deprecated, use component_from_string instead",
321
86
  DeprecationWarning,
322
87
  )
323
- return _module_from_string(
88
+ from reactpy.reactjs.module import string_to_module
89
+
90
+ return string_to_module(
324
91
  name,
325
92
  content,
326
93
  fallback=fallback,
@@ -330,142 +97,6 @@ def module_from_string(
330
97
  )
331
98
 
332
99
 
333
- def _module_from_url(
334
- url: str,
335
- fallback: Any | None = None,
336
- resolve_imports: bool | None = None,
337
- resolve_imports_depth: int = 5,
338
- unmount_before_update: bool = False,
339
- ) -> WebModule:
340
- return WebModule(
341
- source=url,
342
- source_type=URL_SOURCE,
343
- default_fallback=fallback,
344
- file=None,
345
- export_names=(
346
- resolve_module_exports_from_url(url, resolve_imports_depth)
347
- if (
348
- resolve_imports
349
- if resolve_imports is not None
350
- else REACTPY_DEBUG.current
351
- )
352
- else None
353
- ),
354
- unmount_before_update=unmount_before_update,
355
- )
356
-
357
-
358
- def _module_from_file(
359
- name: str,
360
- file: str | Path,
361
- fallback: Any | None = None,
362
- resolve_imports: bool | None = None,
363
- resolve_imports_depth: int = 5,
364
- unmount_before_update: bool = False,
365
- symlink: bool = False,
366
- ) -> WebModule:
367
- name += module_name_suffix(name)
368
-
369
- source_file = Path(file).resolve()
370
- target_file = _web_module_path(name)
371
- if not source_file.exists():
372
- msg = f"Source file does not exist: {source_file}"
373
- raise FileNotFoundError(msg)
374
-
375
- if not target_file.exists():
376
- _copy_file(target_file, source_file, symlink)
377
- elif not _equal_files(source_file, target_file):
378
- logger.info(
379
- f"Existing web module {name!r} will "
380
- f"be replaced with {target_file.resolve()}"
381
- )
382
- target_file.unlink()
383
- _copy_file(target_file, source_file, symlink)
384
-
385
- return WebModule(
386
- source=name,
387
- source_type=NAME_SOURCE,
388
- default_fallback=fallback,
389
- file=target_file,
390
- export_names=(
391
- resolve_module_exports_from_file(source_file, resolve_imports_depth)
392
- if (
393
- resolve_imports
394
- if resolve_imports is not None
395
- else REACTPY_DEBUG.current
396
- )
397
- else None
398
- ),
399
- unmount_before_update=unmount_before_update,
400
- )
401
-
402
-
403
- def _equal_files(f1: Path, f2: Path) -> bool:
404
- f1 = f1.resolve()
405
- f2 = f2.resolve()
406
- return (
407
- (f1.is_symlink() or f2.is_symlink()) and (f1.resolve() == f2.resolve())
408
- ) or filecmp.cmp(str(f1), str(f2), shallow=False)
409
-
410
-
411
- def _copy_file(target: Path, source: Path, symlink: bool) -> None:
412
- target.parent.mkdir(parents=True, exist_ok=True)
413
- if symlink:
414
- target.symlink_to(source)
415
- else:
416
- shutil.copy(source, target)
417
-
418
-
419
- def _module_from_string(
420
- name: str,
421
- content: str,
422
- fallback: Any | None = None,
423
- resolve_imports: bool | None = None,
424
- resolve_imports_depth: int = 5,
425
- unmount_before_update: bool = False,
426
- ) -> WebModule:
427
- name += module_name_suffix(name)
428
-
429
- target_file = _web_module_path(name)
430
-
431
- if target_file.exists() and target_file.read_text(encoding="utf-8") != content:
432
- logger.info(
433
- f"Existing web module {name!r} will "
434
- f"be replaced with {target_file.resolve()}"
435
- )
436
- target_file.unlink()
437
-
438
- target_file.parent.mkdir(parents=True, exist_ok=True)
439
- target_file.write_text(content)
440
-
441
- return WebModule(
442
- source=name,
443
- source_type=NAME_SOURCE,
444
- default_fallback=fallback,
445
- file=target_file,
446
- export_names=(
447
- resolve_module_exports_from_file(target_file, resolve_imports_depth)
448
- if (
449
- resolve_imports
450
- if resolve_imports is not None
451
- else REACTPY_DEBUG.current
452
- )
453
- else None
454
- ),
455
- unmount_before_update=unmount_before_update,
456
- )
457
-
458
-
459
- @dataclass(frozen=True)
460
- class WebModule:
461
- source: str
462
- source_type: SourceType
463
- default_fallback: Any | None
464
- export_names: set[str] | None
465
- file: Path | None
466
- unmount_before_update: bool
467
-
468
-
469
100
  @overload
470
101
  def export(
471
102
  web_module: WebModule,
@@ -491,73 +122,9 @@ def export(
491
122
  allow_children: bool = True,
492
123
  ) -> VdomConstructor | list[VdomConstructor]: # pragma: no cover
493
124
  warn(
494
- "export is deprecated, use reactjs_component_from_* functions instead",
125
+ "export is deprecated, use component_from_* functions instead",
495
126
  DeprecationWarning,
496
127
  )
497
- return _vdom_from_web_module(web_module, export_names, fallback, allow_children)
498
-
499
-
500
- def _vdom_from_web_module(
501
- web_module: WebModule,
502
- export_names: str | list[str] | tuple[str, ...],
503
- fallback: Any | None = None,
504
- allow_children: bool = True,
505
- ) -> VdomConstructor | list[VdomConstructor]:
506
- """Return one or more VDOM constructors from a :class:`WebModule`
507
-
508
- Parameters:
509
- export_names:
510
- One or more names to export. If given as a string, a single component
511
- will be returned. If a list is given, then a list of components will be
512
- returned.
513
- fallback:
514
- What to temporarily display while the module is being loaded.
515
- allow_children:
516
- Whether or not these components can have children.
517
- """
518
- if isinstance(export_names, str):
519
- if (
520
- web_module.export_names is not None
521
- and export_names.split(".")[0] not in web_module.export_names
522
- ):
523
- msg = f"{web_module.source!r} does not export {export_names!r}"
524
- raise ValueError(msg)
525
- return _make_export(web_module, export_names, fallback, allow_children)
526
- else:
527
- if web_module.export_names is not None:
528
- missing = sorted(
529
- {e.split(".")[0] for e in export_names}.difference(
530
- web_module.export_names
531
- )
532
- )
533
- if missing:
534
- msg = f"{web_module.source!r} does not export {missing!r}"
535
- raise ValueError(msg)
536
- return [
537
- _make_export(web_module, name, fallback, allow_children)
538
- for name in export_names
539
- ]
540
-
541
-
542
- def _make_export(
543
- web_module: WebModule,
544
- name: str,
545
- fallback: Any | None,
546
- allow_children: bool,
547
- ) -> VdomConstructor:
548
- return Vdom(
549
- name,
550
- allow_children=allow_children,
551
- import_source=ImportSourceDict(
552
- source=web_module.source,
553
- sourceType=web_module.source_type,
554
- fallback=(fallback or web_module.default_fallback),
555
- unmountBeforeUpdate=web_module.unmount_before_update,
556
- ),
557
- )
558
-
128
+ from reactpy.reactjs.module import module_to_vdom
559
129
 
560
- def _web_module_path(name: str) -> Path:
561
- directory = REACTPY_WEB_MODULES_DIR.current
562
- path = directory.joinpath(*name.split("/"))
563
- return path.with_suffix(path.suffix)
130
+ return module_to_vdom(web_module, export_names, fallback, allow_children)