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