reactpy 2.0.0b4__tar.gz → 2.0.0b6__tar.gz

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 (146) hide show
  1. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/.gitignore +1 -1
  2. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/PKG-INFO +4 -7
  3. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/pyproject.toml +19 -13
  4. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/build_scripts/clean_js_dir.py +12 -11
  5. reactpy-2.0.0b6/src/js/bun.lockb +0 -0
  6. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/js/package.json +1 -1
  7. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/__init__.py +3 -2
  8. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_console/rewrite_props.py +2 -2
  9. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_html.py +11 -9
  10. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_option.py +2 -1
  11. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/config.py +2 -2
  12. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/_life_cycle_hook.py +12 -10
  13. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/_thread_local.py +2 -1
  14. reactpy-2.0.0b6/src/reactpy/core/component.py +32 -0
  15. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/events.py +61 -36
  16. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/hooks.py +25 -35
  17. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/layout.py +193 -201
  18. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/serve.py +17 -22
  19. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/vdom.py +9 -12
  20. reactpy-2.0.0b6/src/reactpy/executors/asgi/__init__.py +10 -0
  21. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/executors/asgi/middleware.py +1 -2
  22. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/executors/asgi/pyscript.py +3 -7
  23. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/executors/asgi/standalone.py +4 -6
  24. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/executors/asgi/types.py +2 -2
  25. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/pyscript/components.py +3 -3
  26. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/pyscript/utils.py +49 -46
  27. reactpy-2.0.0b6/src/reactpy/reactjs/__init__.py +353 -0
  28. reactpy-2.0.0b6/src/reactpy/reactjs/module.py +203 -0
  29. reactpy-2.0.0b6/src/reactpy/reactjs/types.py +7 -0
  30. reactpy-2.0.0b6/src/reactpy/reactjs/utils.py +183 -0
  31. reactpy-2.0.0b6/src/reactpy/static/index-h31022cd.js +5 -0
  32. reactpy-2.0.0b6/src/reactpy/static/index-h31022cd.js.map +11 -0
  33. reactpy-2.0.0b6/src/reactpy/static/index-sbddj6ms.js +5 -0
  34. reactpy-2.0.0b6/src/reactpy/static/index-sbddj6ms.js.map +10 -0
  35. reactpy-2.0.0b6/src/reactpy/static/index-y71bxs88.js +5 -0
  36. reactpy-2.0.0b6/src/reactpy/static/index-y71bxs88.js.map +10 -0
  37. reactpy-2.0.0b6/src/reactpy/static/index.js +4 -0
  38. reactpy-2.0.0b6/src/reactpy/static/index.js.map +18 -0
  39. reactpy-2.0.0b6/src/reactpy/static/react-dom.js +4 -0
  40. reactpy-2.0.0b6/src/reactpy/static/react-dom.js.map +11 -0
  41. reactpy-2.0.0b6/src/reactpy/static/react-jsx-runtime.js +4 -0
  42. reactpy-2.0.0b6/src/reactpy/static/react-jsx-runtime.js.map +9 -0
  43. reactpy-2.0.0b6/src/reactpy/static/react.js +4 -0
  44. reactpy-2.0.0b6/src/reactpy/static/react.js.map +10 -0
  45. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/testing/backend.py +6 -5
  46. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/testing/common.py +3 -5
  47. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/testing/display.py +2 -1
  48. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/testing/logs.py +1 -1
  49. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/transforms.py +2 -2
  50. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/types.py +117 -58
  51. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/utils.py +8 -8
  52. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/web/__init__.py +0 -6
  53. reactpy-2.0.0b6/src/reactpy/web/module.py +130 -0
  54. reactpy-2.0.0b6/src/reactpy/web/utils.py +3 -0
  55. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/widgets.py +2 -2
  56. reactpy-2.0.0b4/src/js/bun.lockb +0 -0
  57. reactpy-2.0.0b4/src/reactpy/core/component.py +0 -66
  58. reactpy-2.0.0b4/src/reactpy/executors/asgi/__init__.py +0 -5
  59. reactpy-2.0.0b4/src/reactpy/static/index.js +0 -4
  60. reactpy-2.0.0b4/src/reactpy/static/index.js.map +0 -22
  61. reactpy-2.0.0b4/src/reactpy/web/module.py +0 -563
  62. reactpy-2.0.0b4/src/reactpy/web/templates/react.js +0 -61
  63. reactpy-2.0.0b4/src/reactpy/web/utils.py +0 -159
  64. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/LICENSE +0 -0
  65. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/README.md +0 -0
  66. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/build_scripts/copy_dir.py +0 -0
  67. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/js/.gitignore +0 -0
  68. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/js/eslint.config.mjs +0 -0
  69. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/js/tsconfig.json +0 -0
  70. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_console/__init__.py +0 -0
  71. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_console/ast_utils.py +0 -0
  72. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_console/cli.py +0 -0
  73. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_console/rewrite_keys.py +0 -0
  74. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/_warnings.py +0 -0
  75. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/__init__.py +0 -0
  76. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/core/_f_back.py +0 -0
  77. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/executors/__init__.py +0 -0
  78. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/executors/utils.py +0 -0
  79. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/logging.py +0 -0
  80. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/py.typed +0 -0
  81. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/pyscript/__init__.py +0 -0
  82. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/pyscript/component_template.py +0 -0
  83. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/pyscript/layout_handler.py +0 -0
  84. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/morphdom/morphdom-esm.js +0 -0
  85. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/morphdom/morphdom-factory.js +0 -0
  86. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/morphdom/morphdom-umd.js +0 -0
  87. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/morphdom/morphdom-umd.min.js +0 -0
  88. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/morphdom/morphdom.js +0 -0
  89. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror-BYspKCDy.js +0 -0
  90. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror-BYspKCDy.js.map +0 -0
  91. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_commands-BLDaEdQ6.js +0 -0
  92. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_commands-BLDaEdQ6.js.map +0 -0
  93. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_lang-python-CkOVBHci.js +0 -0
  94. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_lang-python-CkOVBHci.js.map +0 -0
  95. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_language-DOkvasqm.js +0 -0
  96. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_language-DOkvasqm.js.map +0 -0
  97. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_state-BIAL8JKm.js +0 -0
  98. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_state-BIAL8JKm.js.map +0 -0
  99. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_view-Bt4sLgyA.js +0 -0
  100. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/codemirror_view-Bt4sLgyA.js.map +0 -0
  101. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/core-PTfg6inS.js +0 -0
  102. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/core-PTfg6inS.js.map +0 -0
  103. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/core.css +0 -0
  104. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/core.js +0 -0
  105. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/core.js.map +0 -0
  106. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/deprecations-manager-DIDxhyRq.js +0 -0
  107. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/deprecations-manager-DIDxhyRq.js.map +0 -0
  108. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/donkey-CLhmQOjG.js +0 -0
  109. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/donkey-CLhmQOjG.js.map +0 -0
  110. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/error-uzvvriog.js +0 -0
  111. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/error-uzvvriog.js.map +0 -0
  112. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/index-jZ1aOVVJ.js +0 -0
  113. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/index-jZ1aOVVJ.js.map +0 -0
  114. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/mpy-CnF17tqI.js +0 -0
  115. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/mpy-CnF17tqI.js.map +0 -0
  116. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-BZSSqcx3.js +0 -0
  117. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-BZSSqcx3.js.map +0 -0
  118. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-editor-DZ0Dxzzk.js +0 -0
  119. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-editor-DZ0Dxzzk.js.map +0 -0
  120. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-game-bqieV522.js +0 -0
  121. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-game-bqieV522.js.map +0 -0
  122. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-terminal-DYY4WN57.js +0 -0
  123. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/py-terminal-DYY4WN57.js.map +0 -0
  124. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/service-worker.js +0 -0
  125. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/storage.js +0 -0
  126. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/storage.js.map +0 -0
  127. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/toml-BK2RWy-G.js +0 -0
  128. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/toml-BK2RWy-G.js.map +0 -0
  129. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/toml-Blg7Izee.js +0 -0
  130. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/toml-Blg7Izee.js.map +0 -0
  131. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm-DrSYbXEP.js +0 -0
  132. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm-DrSYbXEP.js.map +0 -0
  133. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm-readline-CK_45Ygx.js +0 -0
  134. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm-readline-CK_45Ygx.js.map +0 -0
  135. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm.css +0 -0
  136. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm_addon-fit--gyF3PcZ.js +0 -0
  137. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm_addon-fit--gyF3PcZ.js.map +0 -0
  138. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm_addon-web-links-D95xh2la.js +0 -0
  139. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/xterm_addon-web-links-D95xh2la.js.map +0 -0
  140. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/zip-pccs084i.js +0 -0
  141. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript/zip-pccs084i.js.map +0 -0
  142. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/static/pyscript-hide-debug.css +0 -0
  143. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/templatetags/__init__.py +0 -0
  144. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/templatetags/jinja.py +0 -0
  145. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/testing/__init__.py +0 -0
  146. {reactpy-2.0.0b4 → reactpy-2.0.0b6}/src/reactpy/testing/utils.py +0 -0
@@ -1,5 +1,5 @@
1
1
  # --- Build Artifacts ---
2
- src/reactpy/static/index.js*
2
+ src/reactpy/static/*.js*
3
3
  src/reactpy/static/morphdom/
4
4
  src/reactpy/static/pyscript/
5
5
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactpy
3
- Version: 2.0.0b4
3
+ Version: 2.0.0b6
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/
@@ -8,28 +8,26 @@ Project-URL: Source, https://github.com/reactive-python/reactpy
8
8
  Author-email: Mark Bakhit <archiethemonger@gmail.com>, Ryan Morshead <ryan.morshead@gmail.com>
9
9
  License-Expression: MIT
10
10
  License-File: LICENSE
11
- Keywords: component,javascript,react,reactpy
11
+ Keywords: asgi,components,interactive,javascript,react,reactive,reactjs,reactpy,server,website,wsgi
12
12
  Classifier: Development Status :: 5 - Production/Stable
13
13
  Classifier: Programming Language :: Python
14
- Classifier: Programming Language :: Python :: 3.10
15
14
  Classifier: Programming Language :: Python :: 3.11
16
15
  Classifier: Programming Language :: Python :: 3.12
17
16
  Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
18
  Classifier: Programming Language :: Python :: Implementation :: CPython
19
19
  Classifier: Programming Language :: Python :: Implementation :: PyPy
20
- Requires-Python: >=3.9
20
+ Requires-Python: >=3.11
21
21
  Requires-Dist: anyio>=3
22
22
  Requires-Dist: fastjsonschema>=2.14.5
23
23
  Requires-Dist: lxml>=4
24
24
  Requires-Dist: requests>=2
25
- Requires-Dist: typing-extensions>=3.10
26
25
  Provides-Extra: all
27
26
  Requires-Dist: asgi-tools; extra == 'all'
28
27
  Requires-Dist: asgiref; extra == 'all'
29
28
  Requires-Dist: jinja2-simple-tags; extra == 'all'
30
29
  Requires-Dist: jinja2>=3; extra == 'all'
31
30
  Requires-Dist: orjson; extra == 'all'
32
- Requires-Dist: pip; extra == 'all'
33
31
  Requires-Dist: playwright; extra == 'all'
34
32
  Requires-Dist: servestatic; extra == 'all'
35
33
  Requires-Dist: uvicorn[standard]; extra == 'all'
@@ -37,7 +35,6 @@ Provides-Extra: asgi
37
35
  Requires-Dist: asgi-tools; extra == 'asgi'
38
36
  Requires-Dist: asgiref; extra == 'asgi'
39
37
  Requires-Dist: orjson; extra == 'asgi'
40
- Requires-Dist: pip; extra == 'asgi'
41
38
  Requires-Dist: servestatic; extra == 'asgi'
42
39
  Provides-Extra: jinja
43
40
  Requires-Dist: jinja2-simple-tags; extra == 'jinja'
@@ -10,30 +10,36 @@ requires = ["hatchling", "hatch-build-scripts"]
10
10
  name = "reactpy"
11
11
  description = "It's React, but in Python."
12
12
  readme = "README.md"
13
- keywords = ["react", "javascript", "reactpy", "component"]
13
+ keywords = [
14
+ "react",
15
+ "reactjs",
16
+ "reactpy",
17
+ "components",
18
+ "asgi",
19
+ "wsgi",
20
+ "website",
21
+ "interactive",
22
+ "reactive",
23
+ "javascript",
24
+ "server",
25
+ ]
14
26
  license = "MIT"
15
27
  authors = [
16
28
  { name = "Mark Bakhit", email = "archiethemonger@gmail.com" },
17
29
  { name = "Ryan Morshead", email = "ryan.morshead@gmail.com" },
18
30
  ]
19
- requires-python = ">=3.9"
31
+ requires-python = ">=3.11"
20
32
  classifiers = [
21
33
  "Development Status :: 5 - Production/Stable",
22
34
  "Programming Language :: Python",
23
- "Programming Language :: Python :: 3.10",
24
35
  "Programming Language :: Python :: 3.11",
25
36
  "Programming Language :: Python :: 3.12",
26
37
  "Programming Language :: Python :: 3.13",
38
+ "Programming Language :: Python :: 3.14",
27
39
  "Programming Language :: Python :: Implementation :: CPython",
28
40
  "Programming Language :: Python :: Implementation :: PyPy",
29
41
  ]
30
- dependencies = [
31
- "fastjsonschema >=2.14.5",
32
- "requests >=2",
33
- "lxml >=4",
34
- "anyio >=3",
35
- "typing-extensions >=3.10",
36
- ]
42
+ dependencies = ["fastjsonschema>=2.14.5", "requests>=2", "lxml>=4", "anyio>=3"]
37
43
  dynamic = ["version"]
38
44
  urls.Changelog = "https://reactpy.dev/docs/about/changelog.html"
39
45
  urls.Documentation = "https://reactpy.dev/"
@@ -41,8 +47,8 @@ urls.Source = "https://github.com/reactive-python/reactpy"
41
47
 
42
48
  [project.optional-dependencies]
43
49
  all = ["reactpy[asgi,jinja,testing]"]
44
- asgi = ["asgiref", "asgi-tools", "servestatic", "orjson", "pip"]
45
- jinja = ["jinja2-simple-tags", "jinja2 >=3"]
50
+ asgi = ["asgiref", "asgi-tools", "servestatic", "orjson"]
51
+ jinja = ["jinja2-simple-tags", "jinja2>=3"]
46
52
  testing = ["playwright", "uvicorn[standard]"]
47
53
 
48
54
  [tool.hatch.version]
@@ -95,7 +101,7 @@ extra-dependencies = [
95
101
  features = ["all"]
96
102
 
97
103
  [[tool.hatch.envs.hatch-test.matrix]]
98
- python = ["3.10", "3.11", "3.12", "3.13"]
104
+ python = ["3.11", "3.12", "3.13", "3.14"]
99
105
 
100
106
  [tool.pytest.ini_options]
101
107
  addopts = ["--strict-config", "--strict-markers"]
@@ -13,29 +13,30 @@ import shutil
13
13
 
14
14
  # Get the path to the JS source directory
15
15
  js_src_dir = pathlib.Path(__file__).parent.parent / "js"
16
-
17
- # Get the paths to all `dist` folders in the JS source directory
18
- dist_dirs = glob.glob(str(js_src_dir / "**/dist"), recursive=True)
19
-
20
- # Get the paths to all `node_modules` folders in the JS source directory
21
- node_modules_dirs = glob.glob(str(js_src_dir / "**/node_modules"), recursive=True)
22
-
23
- # Get the paths to all `tsconfig.tsbuildinfo` files in the JS source directory
24
- tsconfig_tsbuildinfo_files = glob.glob(
25
- str(js_src_dir / "**/tsconfig.tsbuildinfo"), recursive=True
26
- )
16
+ static_output_dir = pathlib.Path(__file__).parent.parent / "reactpy" / "static"
27
17
 
28
18
  # Delete all `dist` folders
19
+ dist_dirs = glob.glob(str(js_src_dir / "**/dist"), recursive=True)
29
20
  for dist_dir in dist_dirs:
30
21
  with contextlib.suppress(FileNotFoundError):
31
22
  shutil.rmtree(dist_dir)
32
23
 
33
24
  # Delete all `node_modules` folders
25
+ node_modules_dirs = glob.glob(str(js_src_dir / "**/node_modules"), recursive=True)
34
26
  for node_modules_dir in node_modules_dirs:
35
27
  with contextlib.suppress(FileNotFoundError):
36
28
  shutil.rmtree(node_modules_dir)
37
29
 
38
30
  # Delete all `tsconfig.tsbuildinfo` files
31
+ tsconfig_tsbuildinfo_files = glob.glob(
32
+ str(js_src_dir / "**/tsconfig.tsbuildinfo"), recursive=True
33
+ )
39
34
  for tsconfig_tsbuildinfo_file in tsconfig_tsbuildinfo_files:
40
35
  with contextlib.suppress(FileNotFoundError):
41
36
  os.remove(tsconfig_tsbuildinfo_file)
37
+
38
+ # Delete all `index-*.js` files
39
+ index_js_files = glob.glob(str(static_output_dir / "index-*.js*"))
40
+ for index_js_file in index_js_files:
41
+ with contextlib.suppress(FileNotFoundError):
42
+ os.remove(index_js_file)
Binary file
@@ -11,7 +11,7 @@
11
11
  "json-pointer": "^0.6.2",
12
12
  "@types/json-pointer": "^1.0.34",
13
13
  "@reactpy/client": "file:./packages/@reactpy/client",
14
- "event-to-object": "file:./packages/event-to-object"
14
+ "event-to-object": "^1.0.1"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@eslint/js": "^9.39.1",
@@ -1,4 +1,4 @@
1
- from reactpy import config, logging, types, web, widgets
1
+ from reactpy import config, logging, reactjs, types, web, widgets
2
2
  from reactpy._html import html
3
3
  from reactpy.core import hooks
4
4
  from reactpy.core.component import component
@@ -23,7 +23,7 @@ from reactpy.pyscript.components import pyscript_component
23
23
  from reactpy.utils import Ref, reactpy_to_string, string_to_reactpy
24
24
 
25
25
  __author__ = "The Reactive Python Team"
26
- __version__ = "2.0.0b4"
26
+ __version__ = "2.0.0b6"
27
27
 
28
28
  __all__ = [
29
29
  "Ref",
@@ -36,6 +36,7 @@ __all__ = [
36
36
  "html",
37
37
  "logging",
38
38
  "pyscript_component",
39
+ "reactjs",
39
40
  "reactpy_to_string",
40
41
  "string_to_reactpy",
41
42
  "types",
@@ -1,10 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import ast
4
+ from collections.abc import Callable
4
5
  from copy import copy
5
6
  from keyword import kwlist
6
7
  from pathlib import Path
7
- from typing import Callable
8
8
 
9
9
  import click
10
10
 
@@ -102,7 +102,7 @@ def _rewrite_props(
102
102
  keys: list[ast.expr | None] = []
103
103
  values: list[ast.expr] = []
104
104
  # Iterate over the keys and values in the dictionary
105
- for k, v in zip(props_node.keys, props_node.values):
105
+ for k, v in zip(props_node.keys, props_node.values, strict=False):
106
106
  if isinstance(k, ast.Constant) and isinstance(k.value, str):
107
107
  # Construct the new key and value
108
108
  k_value, new_v = constructor(k.value, v)
@@ -6,7 +6,6 @@ from typing import ClassVar, overload
6
6
  from reactpy.core.vdom import Vdom
7
7
  from reactpy.types import (
8
8
  EventHandlerDict,
9
- Key,
10
9
  VdomAttributes,
11
10
  VdomChild,
12
11
  VdomChildren,
@@ -14,7 +13,7 @@ from reactpy.types import (
14
13
  VdomDict,
15
14
  )
16
15
 
17
- __all__ = ["html"]
16
+ __all__ = ["h", "html"]
18
17
 
19
18
  NO_CHILDREN_ALLOWED_HTML_BODY = {
20
19
  "area",
@@ -100,12 +99,10 @@ NO_CHILDREN_ALLOWED_SVG = {
100
99
  def _fragment(
101
100
  attributes: VdomAttributes,
102
101
  children: Sequence[VdomChild],
103
- key: Key | None,
104
102
  event_handlers: EventHandlerDict,
105
103
  ) -> VdomDict:
106
104
  """An HTML fragment - this element will not appear in the DOM"""
107
- attributes.pop("key", None)
108
- if attributes or event_handlers:
105
+ if any(k != "key" for k in attributes) or event_handlers:
109
106
  msg = "Fragments cannot have attributes besides 'key'"
110
107
  raise TypeError(msg)
111
108
  model = VdomDict(tagName="")
@@ -113,8 +110,8 @@ def _fragment(
113
110
  if children:
114
111
  model["children"] = children
115
112
 
116
- if key is not None:
117
- model["key"] = key
113
+ if attributes:
114
+ model["attributes"] = attributes
118
115
 
119
116
  return model
120
117
 
@@ -122,7 +119,6 @@ def _fragment(
122
119
  def _script(
123
120
  attributes: VdomAttributes,
124
121
  children: Sequence[VdomChild],
125
- key: Key | None,
126
122
  event_handlers: EventHandlerDict,
127
123
  ) -> VdomDict:
128
124
  """Create a new `<script> <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script>`__ element.
@@ -148,6 +144,8 @@ def _script(
148
144
  msg = "'script' elements do not support event handlers"
149
145
  raise ValueError(msg)
150
146
 
147
+ key = attributes.get("key")
148
+
151
149
  if children:
152
150
  if len(children) > 1:
153
151
  msg = "'script' nodes may have, at most, one child."
@@ -165,7 +163,9 @@ def _script(
165
163
  key = attributes["src"]
166
164
 
167
165
  if key is not None:
168
- model["key"] = key
166
+ if "attributes" not in model:
167
+ model["attributes"] = {}
168
+ model["attributes"]["key"] = key
169
169
 
170
170
  return model
171
171
 
@@ -287,6 +287,7 @@ class HtmlConstructor:
287
287
  "fragment": Vdom("", custom_constructor=_fragment),
288
288
  "svg": SvgConstructor(),
289
289
  }
290
+ __call__ = __cache__["fragment"].__call__
290
291
 
291
292
  def __getattr__(self, value: str) -> VdomConstructor:
292
293
  value = value.rstrip("_").replace("_", "-")
@@ -423,3 +424,4 @@ class HtmlConstructor:
423
424
 
424
425
 
425
426
  html = HtmlConstructor()
427
+ h = html # shorthand alias for html
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from collections.abc import Callable
4
5
  from logging import getLogger
5
- from typing import Any, Callable, Generic, TypeVar, cast
6
+ from typing import Any, Generic, TypeVar, cast
6
7
 
7
8
  from reactpy._warnings import warn
8
9
 
@@ -85,11 +85,11 @@ REACTPY_TESTS_DEFAULT_TIMEOUT = Option(
85
85
 
86
86
  REACTPY_ASYNC_RENDERING = Option(
87
87
  "REACTPY_ASYNC_RENDERING",
88
- default=False,
88
+ default=True,
89
89
  mutable=True,
90
90
  validator=boolean,
91
91
  )
92
- """Whether to render components asynchronously. This is currently an experimental feature."""
92
+ """Whether to render components asynchronously."""
93
93
 
94
94
  REACTPY_RECONNECT_INTERVAL = Option(
95
95
  "REACTPY_RECONNECT_INTERVAL",
@@ -3,13 +3,14 @@ from __future__ import annotations
3
3
  import logging
4
4
  import sys
5
5
  from asyncio import Event, Task, create_task, gather
6
+ from collections.abc import Callable
6
7
  from contextvars import ContextVar, Token
7
- from typing import Any, Callable, Protocol, TypeVar
8
+ from typing import Any, Protocol, TypeVar
8
9
 
9
10
  from anyio import Semaphore
10
11
 
11
12
  from reactpy.core._thread_local import ThreadLocal
12
- from reactpy.types import ComponentType, Context, ContextProviderType
13
+ from reactpy.types import Component, Context, ContextProvider
13
14
  from reactpy.utils import Singleton
14
15
 
15
16
  T = TypeVar("T")
@@ -32,7 +33,10 @@ class _HookStack(Singleton): # nocov
32
33
  )
33
34
 
34
35
  def get(self) -> list[LifeCycleHook]:
35
- return self._state.get()
36
+ try:
37
+ return self._state.get()
38
+ except LookupError:
39
+ return []
36
40
 
37
41
  def initialize(self) -> Token[list[LifeCycleHook]] | None:
38
42
  return None if isinstance(self._state, ThreadLocal) else self._state.set([])
@@ -145,13 +149,13 @@ class LifeCycleHook:
145
149
  "component",
146
150
  )
147
151
 
148
- component: ComponentType
152
+ component: Component
149
153
 
150
154
  def __init__(
151
155
  self,
152
156
  schedule_render: Callable[[], None],
153
157
  ) -> None:
154
- self._context_providers: dict[Context[Any], ContextProviderType[Any]] = {}
158
+ self._context_providers: dict[Context[Any], ContextProvider[Any]] = {}
155
159
  self._schedule_render_callback = schedule_render
156
160
  self._scheduled_render = False
157
161
  self._rendered_atleast_once = False
@@ -200,7 +204,7 @@ class LifeCycleHook:
200
204
  """
201
205
  self._effect_funcs.append(effect_func)
202
206
 
203
- def set_context_provider(self, provider: ContextProviderType[Any]) -> None:
207
+ def set_context_provider(self, provider: ContextProvider[Any]) -> None:
204
208
  """Set a context provider for this hook
205
209
 
206
210
  The context provider will be used to provide state to any child components
@@ -208,9 +212,7 @@ class LifeCycleHook:
208
212
  """
209
213
  self._context_providers[provider.type] = provider
210
214
 
211
- def get_context_provider(
212
- self, context: Context[T]
213
- ) -> ContextProviderType[T] | None:
215
+ def get_context_provider(self, context: Context[T]) -> ContextProvider[T] | None:
214
216
  """Get a context provider for this hook of the given type
215
217
 
216
218
  The context provider will have been set by a parent component. If no provider
@@ -218,7 +220,7 @@ class LifeCycleHook:
218
220
  """
219
221
  return self._context_providers.get(context)
220
222
 
221
- async def affect_component_will_render(self, component: ComponentType) -> None:
223
+ async def affect_component_will_render(self, component: Component) -> None:
222
224
  """The component is about to render"""
223
225
  await self._render_access.acquire()
224
226
  self._scheduled_render = False
@@ -1,5 +1,6 @@
1
+ from collections.abc import Callable
1
2
  from threading import Thread, current_thread
2
- from typing import Callable, Generic, TypeVar
3
+ from typing import Generic, TypeVar
3
4
  from weakref import WeakKeyDictionary
4
5
 
5
6
  _StateType = TypeVar("_StateType")
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import inspect
4
+ from collections.abc import Callable
5
+ from functools import wraps
6
+ from typing import Any
7
+
8
+ from reactpy.types import Component, VdomDict
9
+
10
+
11
+ def component(
12
+ function: Callable[..., Component | VdomDict | str | None],
13
+ ) -> Callable[..., Component]:
14
+ """A decorator for defining a new component.
15
+
16
+ Parameters:
17
+ function: The component's :meth:`reactpy.core.proto.ComponentType.render` function.
18
+ """
19
+ sig = inspect.signature(function)
20
+
21
+ if "key" in sig.parameters and sig.parameters["key"].kind in (
22
+ inspect.Parameter.KEYWORD_ONLY,
23
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
24
+ ):
25
+ msg = f"Component render function {function} uses reserved parameter 'key'"
26
+ raise TypeError(msg)
27
+
28
+ @wraps(function)
29
+ def constructor(*args: Any, key: Any | None = None, **kwargs: Any) -> Component:
30
+ return Component(function, key, args, kwargs, sig)
31
+
32
+ return constructor
@@ -1,13 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import dis
5
- from collections.abc import Sequence
6
- from typing import Any, Callable, Literal, cast, overload
4
+ import inspect
5
+ from collections.abc import Callable, Sequence
6
+ from functools import lru_cache
7
+ from types import CodeType
8
+ from typing import Any, Literal, cast, overload
7
9
 
8
10
  from anyio import create_task_group
9
11
 
10
- from reactpy.types import EventHandlerFunc, EventHandlerType
12
+ from reactpy.types import BaseEventHandler, EventHandlerFunc
11
13
 
12
14
 
13
15
  @overload
@@ -73,7 +75,7 @@ def event(
73
75
  return setup(function) if function is not None else setup
74
76
 
75
77
 
76
- class EventHandler:
78
+ class EventHandler(BaseEventHandler):
77
79
  """Turn a function or coroutine into an event handler
78
80
 
79
81
  Parameters:
@@ -87,14 +89,6 @@ class EventHandler:
87
89
  A unique identifier for this event handler (auto-generated by default)
88
90
  """
89
91
 
90
- __slots__ = (
91
- "__weakref__",
92
- "function",
93
- "prevent_default",
94
- "stop_propagation",
95
- "target",
96
- )
97
-
98
92
  def __init__(
99
93
  self,
100
94
  function: EventHandlerFunc,
@@ -113,26 +107,14 @@ class EventHandler:
113
107
  while hasattr(func_to_inspect, "__wrapped__"):
114
108
  func_to_inspect = func_to_inspect.__wrapped__
115
109
 
116
- code = func_to_inspect.__code__
117
- if code.co_argcount > 0:
118
- event_arg_name = code.co_varnames[0]
119
- last_was_event = False
120
-
121
- for instr in dis.get_instructions(func_to_inspect):
122
- if instr.opname == "LOAD_FAST" and instr.argval == event_arg_name:
123
- last_was_event = True
124
- continue
125
-
126
- if last_was_event and instr.opname in (
127
- "LOAD_METHOD",
128
- "LOAD_ATTR",
129
- ):
130
- if instr.argval == "preventDefault":
131
- self.prevent_default = True
132
- elif instr.argval == "stopPropagation":
133
- self.stop_propagation = True
110
+ found_prevent_default, found_stop_propagation = _inspect_event_handler_code(
111
+ func_to_inspect.__code__
112
+ )
134
113
 
135
- last_was_event = False
114
+ if found_prevent_default:
115
+ self.prevent_default = True
116
+ if found_stop_propagation:
117
+ self.stop_propagation = True
136
118
 
137
119
  __hash__ = None # type: ignore
138
120
 
@@ -168,7 +150,7 @@ def to_event_handler_function(
168
150
  Whether to pass the event parameters a positional args or as a list.
169
151
  """
170
152
  if positional_args:
171
- if asyncio.iscoroutinefunction(function):
153
+ if inspect.iscoroutinefunction(function):
172
154
 
173
155
  async def wrapper(data: Sequence[Any]) -> None:
174
156
  await function(*data)
@@ -182,7 +164,7 @@ def to_event_handler_function(
182
164
 
183
165
  cast(Any, wrapper).__wrapped__ = function
184
166
  return wrapper
185
- elif not asyncio.iscoroutinefunction(function):
167
+ elif not inspect.iscoroutinefunction(function):
186
168
 
187
169
  async def wrapper(data: Sequence[Any]) -> None:
188
170
  function(data)
@@ -194,8 +176,8 @@ def to_event_handler_function(
194
176
 
195
177
 
196
178
  def merge_event_handlers(
197
- event_handlers: Sequence[EventHandlerType],
198
- ) -> EventHandlerType:
179
+ event_handlers: Sequence[BaseEventHandler],
180
+ ) -> BaseEventHandler:
199
181
  """Merge multiple event handlers into one
200
182
 
201
183
  Raises a ValueError if any handlers have conflicting
@@ -247,3 +229,46 @@ def merge_event_handler_funcs(
247
229
  group.start_soon(func, data)
248
230
 
249
231
  return await_all_event_handlers
232
+
233
+
234
+ @lru_cache(maxsize=4096)
235
+ def _inspect_event_handler_code(code: CodeType) -> tuple[bool, bool]:
236
+ prevent_default = False
237
+ stop_propagation = False
238
+
239
+ if code.co_argcount > 0:
240
+ names = code.co_names
241
+ check_prevent_default = "preventDefault" in names
242
+ check_stop_propagation = "stopPropagation" in names
243
+
244
+ if not (check_prevent_default or check_stop_propagation):
245
+ return False, False
246
+
247
+ event_arg_name = code.co_varnames[0]
248
+ last_was_event = False
249
+
250
+ for instr in dis.get_instructions(code):
251
+ if (
252
+ instr.opname in ("LOAD_FAST", "LOAD_FAST_BORROW")
253
+ and instr.argval == event_arg_name
254
+ ):
255
+ last_was_event = True
256
+ continue
257
+
258
+ if last_was_event and instr.opname in (
259
+ "LOAD_METHOD",
260
+ "LOAD_ATTR",
261
+ ):
262
+ if check_prevent_default and instr.argval == "preventDefault":
263
+ prevent_default = True
264
+ check_prevent_default = False
265
+ elif check_stop_propagation and instr.argval == "stopPropagation":
266
+ stop_propagation = True
267
+ check_stop_propagation = False
268
+
269
+ if not (check_prevent_default or check_stop_propagation):
270
+ break
271
+
272
+ last_was_event = False
273
+
274
+ return prevent_default, stop_propagation