reactpy 2.0.0b2__py3-none-any.whl → 2.0.0b4__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 (122) hide show
  1. reactpy/__init__.py +1 -3
  2. reactpy/_console/rewrite_keys.py +2 -4
  3. reactpy/core/component.py +1 -1
  4. reactpy/core/events.py +46 -15
  5. reactpy/core/hooks.py +8 -2
  6. reactpy/core/layout.py +3 -1
  7. reactpy/core/serve.py +3 -22
  8. reactpy/core/vdom.py +0 -1
  9. reactpy/executors/asgi/__init__.py +2 -2
  10. reactpy/executors/asgi/pyscript.py +2 -2
  11. reactpy/executors/utils.py +1 -3
  12. reactpy/pyscript/component_template.py +3 -6
  13. reactpy/pyscript/layout_handler.py +1 -1
  14. reactpy/pyscript/utils.py +1 -7
  15. reactpy/static/index.js +2 -2
  16. reactpy/static/index.js.map +12 -11
  17. reactpy/static/morphdom/morphdom-esm.js +5 -0
  18. reactpy/static/morphdom/morphdom-factory.js +5 -0
  19. reactpy/static/morphdom/morphdom-umd.js +5 -0
  20. reactpy/static/morphdom/morphdom-umd.min.js +1 -1
  21. reactpy/static/morphdom/morphdom.js +5 -0
  22. reactpy/static/pyscript/codemirror-BYspKCDy.js +2 -0
  23. reactpy/static/pyscript/codemirror-BYspKCDy.js.map +1 -0
  24. reactpy/static/pyscript/codemirror_commands-BLDaEdQ6.js +2 -0
  25. reactpy/static/pyscript/codemirror_commands-BLDaEdQ6.js.map +1 -0
  26. reactpy/static/pyscript/codemirror_lang-python-CkOVBHci.js +2 -0
  27. reactpy/static/pyscript/codemirror_lang-python-CkOVBHci.js.map +1 -0
  28. reactpy/static/pyscript/codemirror_language-DOkvasqm.js +2 -0
  29. reactpy/static/pyscript/codemirror_language-DOkvasqm.js.map +1 -0
  30. reactpy/static/pyscript/codemirror_state-BIAL8JKm.js +2 -0
  31. reactpy/static/pyscript/{codemirror_state-1UkzIAPK.js.map → codemirror_state-BIAL8JKm.js.map} +1 -1
  32. reactpy/static/pyscript/codemirror_view-Bt4sLgyA.js +2 -0
  33. reactpy/static/pyscript/codemirror_view-Bt4sLgyA.js.map +1 -0
  34. reactpy/static/pyscript/core-PTfg6inS.js +4 -0
  35. reactpy/static/pyscript/core-PTfg6inS.js.map +1 -0
  36. reactpy/static/pyscript/core.css +1 -1
  37. reactpy/static/pyscript/core.js +1 -1
  38. reactpy/static/pyscript/{deprecations-manager-B8Tz2RBD.js → deprecations-manager-DIDxhyRq.js} +2 -2
  39. reactpy/static/pyscript/{deprecations-manager-B8Tz2RBD.js.map → deprecations-manager-DIDxhyRq.js.map} +1 -1
  40. reactpy/static/pyscript/{donkey-83vCPSep.js → donkey-CLhmQOjG.js} +2 -2
  41. reactpy/static/pyscript/{donkey-83vCPSep.js.map → donkey-CLhmQOjG.js.map} +1 -1
  42. reactpy/static/pyscript/error-uzvvriog.js +2 -0
  43. reactpy/static/pyscript/{error-Bd5T9j4s.js.map → error-uzvvriog.js.map} +1 -1
  44. reactpy/static/pyscript/index-jZ1aOVVJ.js +2 -0
  45. reactpy/static/pyscript/index-jZ1aOVVJ.js.map +1 -0
  46. reactpy/static/pyscript/mpy-CnF17tqI.js +2 -0
  47. reactpy/static/pyscript/mpy-CnF17tqI.js.map +1 -0
  48. reactpy/static/pyscript/py-BZSSqcx3.js +2 -0
  49. reactpy/static/pyscript/{py-CdW1_TfR.js.map → py-BZSSqcx3.js.map} +1 -1
  50. reactpy/static/pyscript/py-editor-DZ0Dxzzk.js +2 -0
  51. reactpy/static/pyscript/py-editor-DZ0Dxzzk.js.map +1 -0
  52. reactpy/static/pyscript/py-game-bqieV522.js +2 -0
  53. reactpy/static/pyscript/py-game-bqieV522.js.map +1 -0
  54. reactpy/static/pyscript/py-terminal-DYY4WN57.js +2 -0
  55. reactpy/static/pyscript/{py-terminal-C3QERaDG.js.map → py-terminal-DYY4WN57.js.map} +1 -1
  56. reactpy/static/pyscript/service-worker.js +1 -0
  57. reactpy/static/pyscript/storage.js +1 -1
  58. reactpy/static/pyscript/storage.js.map +1 -1
  59. reactpy/static/pyscript/toml-BK2RWy-G.js +3 -0
  60. reactpy/static/pyscript/{toml-DiUM0_qs.js.map → toml-BK2RWy-G.js.map} +1 -1
  61. reactpy/static/pyscript/toml-Blg7Izee.js +3 -0
  62. reactpy/static/pyscript/toml-Blg7Izee.js.map +1 -0
  63. reactpy/static/pyscript/xterm-DrSYbXEP.js +2 -0
  64. reactpy/static/pyscript/xterm-DrSYbXEP.js.map +1 -0
  65. reactpy/static/pyscript/xterm-readline-CK_45Ygx.js +2 -0
  66. reactpy/static/pyscript/xterm-readline-CK_45Ygx.js.map +1 -0
  67. reactpy/static/pyscript/xterm.css +1 -1
  68. reactpy/static/pyscript/xterm_addon-fit--gyF3PcZ.js.map +1 -1
  69. reactpy/static/pyscript/xterm_addon-web-links-D95xh2la.js +2 -0
  70. reactpy/static/pyscript/xterm_addon-web-links-D95xh2la.js.map +1 -0
  71. reactpy/static/pyscript/zip-pccs084i.js +2 -0
  72. reactpy/static/pyscript/zip-pccs084i.js.map +1 -0
  73. reactpy/templatetags/__init__.py +2 -2
  74. reactpy/templatetags/jinja.py +1 -1
  75. reactpy/testing/backend.py +1 -1
  76. reactpy/types.py +16 -0
  77. reactpy/utils.py +5 -5
  78. reactpy/web/__init__.py +6 -0
  79. reactpy/web/module.py +320 -62
  80. reactpy/web/utils.py +1 -1
  81. reactpy/widgets.py +3 -25
  82. {reactpy-2.0.0b2.dist-info → reactpy-2.0.0b4.dist-info}/METADATA +2 -3
  83. reactpy-2.0.0b4.dist-info/RECORD +116 -0
  84. {reactpy-2.0.0b2.dist-info → reactpy-2.0.0b4.dist-info}/WHEEL +1 -1
  85. reactpy/static/pyscript/codemirror-DNtCVICy.js +0 -2
  86. reactpy/static/pyscript/codemirror-DNtCVICy.js.map +0 -1
  87. reactpy/static/pyscript/codemirror_commands-DvCG1qNv.js +0 -2
  88. reactpy/static/pyscript/codemirror_commands-DvCG1qNv.js.map +0 -1
  89. reactpy/static/pyscript/codemirror_lang-python-BgZ6XoEX.js +0 -2
  90. reactpy/static/pyscript/codemirror_lang-python-BgZ6XoEX.js.map +0 -1
  91. reactpy/static/pyscript/codemirror_language-ddx8nifT.js +0 -2
  92. reactpy/static/pyscript/codemirror_language-ddx8nifT.js.map +0 -1
  93. reactpy/static/pyscript/codemirror_state-1UkzIAPK.js +0 -2
  94. reactpy/static/pyscript/codemirror_view-C2ObO--u.js +0 -2
  95. reactpy/static/pyscript/codemirror_view-C2ObO--u.js.map +0 -1
  96. reactpy/static/pyscript/core-BsIVS0z4.js +0 -4
  97. reactpy/static/pyscript/core-BsIVS0z4.js.map +0 -1
  98. reactpy/static/pyscript/error-Bd5T9j4s.js +0 -2
  99. reactpy/static/pyscript/index-978lpDXp.js +0 -2
  100. reactpy/static/pyscript/index-978lpDXp.js.map +0 -1
  101. reactpy/static/pyscript/mpy-De_qSCIU.js +0 -2
  102. reactpy/static/pyscript/mpy-De_qSCIU.js.map +0 -1
  103. reactpy/static/pyscript/py-CdW1_TfR.js +0 -2
  104. reactpy/static/pyscript/py-editor-MNjBwLfD.js +0 -2
  105. reactpy/static/pyscript/py-editor-MNjBwLfD.js.map +0 -1
  106. reactpy/static/pyscript/py-game-Did5BuBI.js +0 -2
  107. reactpy/static/pyscript/py-game-Did5BuBI.js.map +0 -1
  108. reactpy/static/pyscript/py-terminal-C3QERaDG.js +0 -2
  109. reactpy/static/pyscript/toml-BLBSZ43A.js +0 -3
  110. reactpy/static/pyscript/toml-BLBSZ43A.js.map +0 -1
  111. reactpy/static/pyscript/toml-DiUM0_qs.js +0 -3
  112. reactpy/static/pyscript/xterm-7LwxAMsn.js +0 -2
  113. reactpy/static/pyscript/xterm-7LwxAMsn.js.map +0 -1
  114. reactpy/static/pyscript/xterm-readline-e8QPhSrm.js +0 -2
  115. reactpy/static/pyscript/xterm-readline-e8QPhSrm.js.map +0 -1
  116. reactpy/static/pyscript/xterm_addon-web-links-Cnej-nJ6.js +0 -2
  117. reactpy/static/pyscript/xterm_addon-web-links-Cnej-nJ6.js.map +0 -1
  118. reactpy/static/pyscript/zip-CFEEYRfx.js +0 -2
  119. reactpy/static/pyscript/zip-CFEEYRfx.js.map +0 -1
  120. reactpy-2.0.0b2.dist-info/RECORD +0 -115
  121. {reactpy-2.0.0b2.dist-info → reactpy-2.0.0b4.dist-info}/entry_points.txt +0 -0
  122. {reactpy-2.0.0b2.dist-info → reactpy-2.0.0b4.dist-info}/licenses/LICENSE +0 -0
@@ -1,3 +1,3 @@
1
- from reactpy.templatetags.jinja import Jinja
1
+ from reactpy.templatetags.jinja import ReactPyJinja
2
2
 
3
- __all__ = ["Jinja"]
3
+ __all__ = ["ReactPyJinja"]
@@ -7,7 +7,7 @@ from reactpy.executors.utils import server_side_component_html
7
7
  from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html
8
8
 
9
9
 
10
- class Jinja(StandaloneTag): # type: ignore
10
+ class ReactPyJinja(StandaloneTag): # type: ignore
11
11
  safe_output = True
12
12
  tags: ClassVar[set[str]] = {"component", "pyscript_component", "pyscript_setup"}
13
13
 
@@ -119,7 +119,7 @@ class BackendFixture:
119
119
  self.log_records = self._exit_stack.enter_context(capture_reactpy_logs())
120
120
 
121
121
  # Wait for the server to start
122
- self.webserver.config.setup_event_loop()
122
+ self.webserver.config.get_loop_factory()
123
123
  self.webserver_task = asyncio.create_task(self.webserver.serve())
124
124
  await asyncio.sleep(1)
125
125
 
reactpy/types.py CHANGED
@@ -1071,3 +1071,19 @@ class CustomVdomConstructor(Protocol):
1071
1071
  class EllipsisRepr:
1072
1072
  def __repr__(self) -> str:
1073
1073
  return "..."
1074
+
1075
+
1076
+ class Event(dict):
1077
+ """
1078
+ A light `dict` wrapper for event data passed to event handler functions.
1079
+ """
1080
+
1081
+ def __getattr__(self, name: str) -> Any:
1082
+ value = self.get(name)
1083
+ return Event(value) if isinstance(value, dict) else value
1084
+
1085
+ def preventDefault(self) -> None:
1086
+ """Prevent the default action of the event."""
1087
+
1088
+ def stopPropagation(self) -> None:
1089
+ """Stop the event from propagating."""
reactpy/utils.py CHANGED
@@ -45,6 +45,8 @@ class Ref(Generic[_RefValue]):
45
45
  self.current = new
46
46
  return old
47
47
 
48
+ __hash__ = None # type: ignore
49
+
48
50
  def __eq__(self, other: object) -> bool:
49
51
  try:
50
52
  return isinstance(other, Ref) and (other.current == self.current)
@@ -111,19 +113,17 @@ def string_to_reactpy(
111
113
  try:
112
114
  root_node: etree._Element = fromstring(
113
115
  html.strip(),
114
- parser=etree.HTMLParser(
116
+ parser=etree.HTMLParser( # type: ignore
115
117
  remove_comments=True,
116
118
  remove_pis=True,
117
119
  remove_blank_text=True,
118
120
  recover=not strict,
119
121
  ),
120
122
  )
121
- except etree.XMLSyntaxError as e:
122
- if not strict:
123
- raise e # nocov
123
+ except Exception as e:
124
124
  msg = (
125
125
  "An error has occurred while parsing the HTML.\n\n"
126
- "This HTML may be malformatted, or may not perfectly adhere to HTML5.\n"
126
+ "This HTML may be malformatted, or may not adhere to the HTML5 spec.\n"
127
127
  "If you believe the exception above was due to something intentional, you "
128
128
  "can disable the strict parameter on string_to_reactpy().\n"
129
129
  "Otherwise, repair your broken HTML and try again."
reactpy/web/__init__.py CHANGED
@@ -3,6 +3,9 @@ from reactpy.web.module import (
3
3
  module_from_file,
4
4
  module_from_string,
5
5
  module_from_url,
6
+ reactjs_component_from_file,
7
+ reactjs_component_from_string,
8
+ reactjs_component_from_url,
6
9
  )
7
10
 
8
11
  __all__ = [
@@ -10,4 +13,7 @@ __all__ = [
10
13
  "module_from_file",
11
14
  "module_from_string",
12
15
  "module_from_url",
16
+ "reactjs_component_from_file",
17
+ "reactjs_component_from_string",
18
+ "reactjs_component_from_url",
13
19
  ]
reactpy/web/module.py CHANGED
@@ -7,6 +7,7 @@ from dataclasses import dataclass
7
7
  from pathlib import Path
8
8
  from typing import Any, NewType, overload
9
9
 
10
+ from reactpy._warnings import warn
10
11
  from reactpy.config import REACTPY_DEBUG, REACTPY_WEB_MODULES_DIR
11
12
  from reactpy.core.vdom import Vdom
12
13
  from reactpy.types import ImportSourceDict, VdomConstructor
@@ -27,71 +28,138 @@ URL_SOURCE = SourceType("URL")
27
28
  """A source loaded from a URL, usually a CDN"""
28
29
 
29
30
 
30
- def module_from_url(
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(
31
61
  url: str,
62
+ import_names: str | list[str] | tuple[str, ...],
32
63
  fallback: Any | None = None,
33
- resolve_exports: bool | None = None,
34
- resolve_exports_depth: int = 5,
64
+ resolve_imports: bool | None = None,
65
+ resolve_imports_depth: int = 5,
35
66
  unmount_before_update: bool = False,
36
- ) -> WebModule:
37
- """Load a :class:`WebModule` from a :data:`URL_SOURCE`
67
+ allow_children: bool = True,
68
+ ) -> VdomConstructor | list[VdomConstructor]:
69
+ """Import a component from a URL.
38
70
 
39
71
  Parameters:
40
72
  url:
41
- Where the javascript module will be loaded from which conforms to the
42
- interface for :ref:`Custom Javascript Components`
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.
43
78
  fallback:
44
79
  What to temporarily display while the module is being loaded.
45
80
  resolve_imports:
46
- Whether to try and find all the named exports of this module.
47
- resolve_exports_depth:
48
- How deeply to search for those exports.
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.
49
84
  unmount_before_update:
50
85
  Cause the component to be unmounted before each update. This option should
51
86
  only be used if the imported package fails to re-render when props change.
52
87
  Using this option has negative performance consequences since all DOM
53
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.
54
91
  """
55
- return WebModule(
56
- source=url,
57
- source_type=URL_SOURCE,
58
- default_fallback=fallback,
59
- file=None,
60
- export_names=(
61
- resolve_module_exports_from_url(url, resolve_exports_depth)
62
- if (
63
- resolve_exports
64
- if resolve_exports is not None
65
- else REACTPY_DEBUG.current
66
- )
67
- else None
68
- ),
69
- unmount_before_update=unmount_before_update,
70
- )
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)
71
105
 
72
106
 
73
- def module_from_file(
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(
74
123
  name: str,
75
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, ...],
76
139
  fallback: Any | None = None,
77
- resolve_exports: bool | None = None,
78
- resolve_exports_depth: int = 5,
140
+ resolve_imports: bool | None = None,
141
+ resolve_imports_depth: int = 5,
79
142
  unmount_before_update: bool = False,
80
143
  symlink: bool = False,
81
- ) -> WebModule:
82
- """Load a :class:`WebModule` from a given ``file``
144
+ allow_children: bool = True,
145
+ ) -> VdomConstructor | list[VdomConstructor]:
146
+ """Import a component from a file.
83
147
 
84
148
  Parameters:
85
149
  name:
86
150
  The name of the package
87
151
  file:
88
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.
89
157
  fallback:
90
158
  What to temporarily display while the module is being loaded.
91
159
  resolve_imports:
92
- Whether to try and find all the named exports of this module.
93
- resolve_exports_depth:
94
- How deeply to search for those exports.
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.
95
163
  unmount_before_update:
96
164
  Cause the component to be unmounted before each update. This option should
97
165
  only be used if the imported package fails to re-render when props change.
@@ -99,7 +167,203 @@ def module_from_file(
99
167
  elements must be changed on each render. See :issue:`461` for more info.
100
168
  symlink:
101
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.
102
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)
265
+
266
+
267
+ def module_from_url(
268
+ url: str,
269
+ fallback: Any | None = None,
270
+ resolve_exports: bool | None = None,
271
+ resolve_exports_depth: int = 5,
272
+ unmount_before_update: bool = False,
273
+ ) -> WebModule: # pragma: no cover
274
+ warn(
275
+ "module_from_url is deprecated, use reactjs_component_from_url instead",
276
+ DeprecationWarning,
277
+ )
278
+ return _module_from_url(
279
+ url,
280
+ fallback=fallback,
281
+ resolve_imports=resolve_exports,
282
+ resolve_imports_depth=resolve_exports_depth,
283
+ unmount_before_update=unmount_before_update,
284
+ )
285
+
286
+
287
+ def module_from_file(
288
+ name: str,
289
+ file: str | Path,
290
+ fallback: Any | None = None,
291
+ resolve_exports: bool | None = None,
292
+ resolve_exports_depth: int = 5,
293
+ unmount_before_update: bool = False,
294
+ symlink: bool = False,
295
+ ) -> WebModule: # pragma: no cover
296
+ warn(
297
+ "module_from_file is deprecated, use reactjs_component_from_file instead",
298
+ DeprecationWarning,
299
+ )
300
+ return _module_from_file(
301
+ name,
302
+ file,
303
+ fallback=fallback,
304
+ resolve_imports=resolve_exports,
305
+ resolve_imports_depth=resolve_exports_depth,
306
+ unmount_before_update=unmount_before_update,
307
+ symlink=symlink,
308
+ )
309
+
310
+
311
+ def module_from_string(
312
+ name: str,
313
+ content: str,
314
+ fallback: Any | None = None,
315
+ resolve_exports: bool | None = None,
316
+ resolve_exports_depth: int = 5,
317
+ unmount_before_update: bool = False,
318
+ ) -> WebModule: # pragma: no cover
319
+ warn(
320
+ "module_from_string is deprecated, use reactjs_component_from_string instead",
321
+ DeprecationWarning,
322
+ )
323
+ return _module_from_string(
324
+ name,
325
+ content,
326
+ fallback=fallback,
327
+ resolve_imports=resolve_exports,
328
+ resolve_imports_depth=resolve_exports_depth,
329
+ unmount_before_update=unmount_before_update,
330
+ )
331
+
332
+
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:
103
367
  name += module_name_suffix(name)
104
368
 
105
369
  source_file = Path(file).resolve()
@@ -124,10 +388,10 @@ def module_from_file(
124
388
  default_fallback=fallback,
125
389
  file=target_file,
126
390
  export_names=(
127
- resolve_module_exports_from_file(source_file, resolve_exports_depth)
391
+ resolve_module_exports_from_file(source_file, resolve_imports_depth)
128
392
  if (
129
- resolve_exports
130
- if resolve_exports is not None
393
+ resolve_imports
394
+ if resolve_imports is not None
131
395
  else REACTPY_DEBUG.current
132
396
  )
133
397
  else None
@@ -152,33 +416,14 @@ def _copy_file(target: Path, source: Path, symlink: bool) -> None:
152
416
  shutil.copy(source, target)
153
417
 
154
418
 
155
- def module_from_string(
419
+ def _module_from_string(
156
420
  name: str,
157
421
  content: str,
158
422
  fallback: Any | None = None,
159
- resolve_exports: bool | None = None,
160
- resolve_exports_depth: int = 5,
423
+ resolve_imports: bool | None = None,
424
+ resolve_imports_depth: int = 5,
161
425
  unmount_before_update: bool = False,
162
426
  ) -> WebModule:
163
- """Load a :class:`WebModule` whose ``content`` comes from a string.
164
-
165
- Parameters:
166
- name:
167
- The name of the package
168
- content:
169
- The contents of the web module
170
- fallback:
171
- What to temporarily display while the module is being loaded.
172
- resolve_imports:
173
- Whether to try and find all the named exports of this module.
174
- resolve_exports_depth:
175
- How deeply to search for those exports.
176
- unmount_before_update:
177
- Cause the component to be unmounted before each update. This option should
178
- only be used if the imported package fails to re-render when props change.
179
- Using this option has negative performance consequences since all DOM
180
- elements must be changed on each render. See :issue:`461` for more info.
181
- """
182
427
  name += module_name_suffix(name)
183
428
 
184
429
  target_file = _web_module_path(name)
@@ -199,10 +444,10 @@ def module_from_string(
199
444
  default_fallback=fallback,
200
445
  file=target_file,
201
446
  export_names=(
202
- resolve_module_exports_from_file(target_file, resolve_exports_depth)
447
+ resolve_module_exports_from_file(target_file, resolve_imports_depth)
203
448
  if (
204
- resolve_exports
205
- if resolve_exports is not None
449
+ resolve_imports
450
+ if resolve_imports is not None
206
451
  else REACTPY_DEBUG.current
207
452
  )
208
453
  else None
@@ -244,6 +489,19 @@ def export(
244
489
  export_names: str | list[str] | tuple[str, ...],
245
490
  fallback: Any | None = None,
246
491
  allow_children: bool = True,
492
+ ) -> VdomConstructor | list[VdomConstructor]: # pragma: no cover
493
+ warn(
494
+ "export is deprecated, use reactjs_component_from_* functions instead",
495
+ DeprecationWarning,
496
+ )
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,
247
505
  ) -> VdomConstructor | list[VdomConstructor]:
248
506
  """Return one or more VDOM constructors from a :class:`WebModule`
249
507
 
reactpy/web/utils.py CHANGED
@@ -12,7 +12,7 @@ def module_name_suffix(name: str) -> str:
12
12
  if name.startswith("@"):
13
13
  name = name[1:]
14
14
  head, _, tail = name.partition("@") # handle version identifier
15
- version, _, tail = tail.partition("/") # get section after version
15
+ _, _, tail = tail.partition("/") # get section after version
16
16
  return PurePosixPath(tail or head).suffix or ".js"
17
17
 
18
18
 
reactpy/widgets.py CHANGED
@@ -2,12 +2,11 @@ from __future__ import annotations
2
2
 
3
3
  from base64 import b64encode
4
4
  from collections.abc import Sequence
5
- from typing import TYPE_CHECKING, Any, Callable, Protocol, TypeVar
5
+ from typing import Any, Callable, Protocol, TypeVar
6
6
 
7
7
  import reactpy
8
8
  from reactpy._html import html
9
- from reactpy._warnings import warn
10
- from reactpy.types import ComponentConstructor, VdomAttributes, VdomDict
9
+ from reactpy.types import VdomAttributes, VdomDict
11
10
 
12
11
 
13
12
  def image(
@@ -22,11 +21,7 @@ def image(
22
21
  if format == "svg":
23
22
  format = "svg+xml" # noqa: A001
24
23
 
25
- if isinstance(value, str):
26
- bytes_value = value.encode()
27
- else:
28
- bytes_value = value
29
-
24
+ bytes_value = value.encode() if isinstance(value, str) else value
30
25
  base64_value = b64encode(bytes_value).decode()
31
26
  src = f"data:image/{format};base64,{base64_value}"
32
27
 
@@ -83,20 +78,3 @@ _CastTo_co = TypeVar("_CastTo_co", covariant=True)
83
78
 
84
79
  class _CastFunc(Protocol[_CastTo_co]):
85
80
  def __call__(self, value: str) -> _CastTo_co: ...
86
-
87
-
88
- if TYPE_CHECKING:
89
- from reactpy.testing.backend import _MountFunc
90
-
91
-
92
- def hotswap(
93
- update_on_change: bool = False,
94
- ) -> tuple[_MountFunc, ComponentConstructor]: # nocov
95
- warn(
96
- "The 'hotswap' function is deprecated and will be removed in a future release",
97
- DeprecationWarning,
98
- stacklevel=2,
99
- )
100
- from reactpy.testing.backend import _hotswap
101
-
102
- return _hotswap(update_on_change)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactpy
3
- Version: 2.0.0b2
3
+ Version: 2.0.0b4
4
4
  Summary: It's React, but in Python.
5
5
  Project-URL: Changelog, https://reactpy.dev/docs/about/changelog.html
6
6
  Project-URL: Documentation, https://reactpy.dev/
@@ -44,8 +44,7 @@ Requires-Dist: jinja2-simple-tags; extra == 'jinja'
44
44
  Requires-Dist: jinja2>=3; extra == 'jinja'
45
45
  Provides-Extra: testing
46
46
  Requires-Dist: playwright; extra == 'testing'
47
- Provides-Extra: uvicorn
48
- Requires-Dist: uvicorn[standard]; extra == 'uvicorn'
47
+ Requires-Dist: uvicorn[standard]; extra == 'testing'
49
48
  Description-Content-Type: text/markdown
50
49
 
51
50
  # <img src="https://raw.githubusercontent.com/reactive-python/reactpy/main/branding/svg/reactpy-logo-square.svg" align="left" height="45"/> ReactPy