reactpy 2.0.0b6__tar.gz → 2.0.0b8__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 (150) hide show
  1. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/PKG-INFO +1 -1
  2. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/pyproject.toml +41 -33
  3. reactpy-2.0.0b8/src/build_scripts/build_js_app.py +29 -0
  4. reactpy-2.0.0b8/src/build_scripts/build_js_client.py +29 -0
  5. reactpy-2.0.0b8/src/build_scripts/build_js_event_to_object.py +29 -0
  6. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/build_scripts/clean_js_dir.py +2 -0
  7. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/build_scripts/copy_dir.py +1 -0
  8. reactpy-2.0.0b8/src/build_scripts/delete_old_coverage.py +21 -0
  9. reactpy-2.0.0b8/src/build_scripts/install_playwright.py +17 -0
  10. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/js/bun.lockb +0 -0
  11. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/js/package.json +3 -2
  12. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/js/tsconfig.json +0 -6
  13. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/__init__.py +2 -2
  14. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/config.py +1 -1
  15. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/_thread_local.py +1 -1
  16. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/hooks.py +34 -37
  17. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/asgi/pyscript.py +4 -1
  18. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/asgi/standalone.py +1 -1
  19. {reactpy-2.0.0b6/src/reactpy → reactpy-2.0.0b8/src/reactpy/executors}/pyscript/component_template.py +1 -1
  20. {reactpy-2.0.0b6/src/reactpy → reactpy-2.0.0b8/src/reactpy/executors}/pyscript/components.py +1 -1
  21. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/utils.py +32 -7
  22. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/reactjs/__init__.py +5 -7
  23. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/reactjs/module.py +106 -42
  24. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/reactjs/utils.py +49 -20
  25. reactpy-2.0.0b6/src/reactpy/static/index-sbddj6ms.js → reactpy-2.0.0b8/src/reactpy/static/index-64wy0fss.js +4 -4
  26. reactpy-2.0.0b6/src/reactpy/static/index-sbddj6ms.js.map → reactpy-2.0.0b8/src/reactpy/static/index-64wy0fss.js.map +1 -1
  27. reactpy-2.0.0b8/src/reactpy/static/index-beq660xy.js +5 -0
  28. reactpy-2.0.0b8/src/reactpy/static/index-beq660xy.js.map +12 -0
  29. reactpy-2.0.0b8/src/reactpy/static/index.js +4 -0
  30. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/index.js.map +6 -5
  31. reactpy-2.0.0b8/src/reactpy/static/preact-dom.js +4 -0
  32. reactpy-2.0.0b6/src/reactpy/static/react-dom.js.map → reactpy-2.0.0b8/src/reactpy/static/preact-dom.js.map +3 -3
  33. reactpy-2.0.0b8/src/reactpy/static/preact-jsx-runtime.js +4 -0
  34. reactpy-2.0.0b6/src/reactpy/static/react-jsx-runtime.js.map → reactpy-2.0.0b8/src/reactpy/static/preact-jsx-runtime.js.map +1 -1
  35. reactpy-2.0.0b8/src/reactpy/static/preact.js +4 -0
  36. reactpy-2.0.0b6/src/reactpy/static/react.js.map → reactpy-2.0.0b8/src/reactpy/static/preact.js.map +3 -3
  37. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/templatetags/jinja.py +4 -1
  38. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/testing/__init__.py +2 -7
  39. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/testing/backend.py +20 -8
  40. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/testing/common.py +1 -9
  41. reactpy-2.0.0b8/src/reactpy/testing/display.py +111 -0
  42. reactpy-2.0.0b6/src/reactpy/static/index-h31022cd.js +0 -5
  43. reactpy-2.0.0b6/src/reactpy/static/index-h31022cd.js.map +0 -11
  44. reactpy-2.0.0b6/src/reactpy/static/index-y71bxs88.js +0 -5
  45. reactpy-2.0.0b6/src/reactpy/static/index-y71bxs88.js.map +0 -10
  46. reactpy-2.0.0b6/src/reactpy/static/index.js +0 -4
  47. reactpy-2.0.0b6/src/reactpy/static/react-dom.js +0 -4
  48. reactpy-2.0.0b6/src/reactpy/static/react-jsx-runtime.js +0 -4
  49. reactpy-2.0.0b6/src/reactpy/static/react.js +0 -4
  50. reactpy-2.0.0b6/src/reactpy/testing/display.py +0 -75
  51. reactpy-2.0.0b6/src/reactpy/testing/utils.py +0 -27
  52. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/.gitignore +0 -0
  53. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/LICENSE +0 -0
  54. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/README.md +0 -0
  55. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/js/.gitignore +0 -0
  56. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/js/eslint.config.mjs +0 -0
  57. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_console/__init__.py +0 -0
  58. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_console/ast_utils.py +0 -0
  59. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_console/cli.py +0 -0
  60. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_console/rewrite_keys.py +0 -0
  61. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_console/rewrite_props.py +0 -0
  62. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_html.py +0 -0
  63. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_option.py +0 -0
  64. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/_warnings.py +0 -0
  65. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/__init__.py +0 -0
  66. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/_f_back.py +0 -0
  67. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/_life_cycle_hook.py +0 -0
  68. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/component.py +0 -0
  69. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/events.py +0 -0
  70. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/layout.py +0 -0
  71. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/serve.py +0 -0
  72. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/core/vdom.py +0 -0
  73. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/__init__.py +0 -0
  74. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/asgi/__init__.py +0 -0
  75. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/asgi/middleware.py +0 -0
  76. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/executors/asgi/types.py +0 -0
  77. {reactpy-2.0.0b6/src/reactpy → reactpy-2.0.0b8/src/reactpy/executors}/pyscript/__init__.py +0 -0
  78. {reactpy-2.0.0b6/src/reactpy → reactpy-2.0.0b8/src/reactpy/executors}/pyscript/layout_handler.py +0 -0
  79. {reactpy-2.0.0b6/src/reactpy → reactpy-2.0.0b8/src/reactpy/executors}/pyscript/utils.py +0 -0
  80. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/logging.py +0 -0
  81. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/py.typed +0 -0
  82. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/reactjs/types.py +0 -0
  83. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/morphdom/morphdom-esm.js +0 -0
  84. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/morphdom/morphdom-factory.js +0 -0
  85. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/morphdom/morphdom-umd.js +0 -0
  86. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/morphdom/morphdom-umd.min.js +0 -0
  87. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/morphdom/morphdom.js +0 -0
  88. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror-BYspKCDy.js +0 -0
  89. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror-BYspKCDy.js.map +0 -0
  90. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_commands-BLDaEdQ6.js +0 -0
  91. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_commands-BLDaEdQ6.js.map +0 -0
  92. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_lang-python-CkOVBHci.js +0 -0
  93. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_lang-python-CkOVBHci.js.map +0 -0
  94. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_language-DOkvasqm.js +0 -0
  95. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_language-DOkvasqm.js.map +0 -0
  96. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_state-BIAL8JKm.js +0 -0
  97. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_state-BIAL8JKm.js.map +0 -0
  98. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_view-Bt4sLgyA.js +0 -0
  99. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/codemirror_view-Bt4sLgyA.js.map +0 -0
  100. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/core-PTfg6inS.js +0 -0
  101. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/core-PTfg6inS.js.map +0 -0
  102. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/core.css +0 -0
  103. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/core.js +0 -0
  104. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/core.js.map +0 -0
  105. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/deprecations-manager-DIDxhyRq.js +0 -0
  106. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/deprecations-manager-DIDxhyRq.js.map +0 -0
  107. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/donkey-CLhmQOjG.js +0 -0
  108. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/donkey-CLhmQOjG.js.map +0 -0
  109. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/error-uzvvriog.js +0 -0
  110. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/error-uzvvriog.js.map +0 -0
  111. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/index-jZ1aOVVJ.js +0 -0
  112. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/index-jZ1aOVVJ.js.map +0 -0
  113. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/mpy-CnF17tqI.js +0 -0
  114. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/mpy-CnF17tqI.js.map +0 -0
  115. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-BZSSqcx3.js +0 -0
  116. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-BZSSqcx3.js.map +0 -0
  117. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-editor-DZ0Dxzzk.js +0 -0
  118. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-editor-DZ0Dxzzk.js.map +0 -0
  119. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-game-bqieV522.js +0 -0
  120. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-game-bqieV522.js.map +0 -0
  121. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-terminal-DYY4WN57.js +0 -0
  122. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/py-terminal-DYY4WN57.js.map +0 -0
  123. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/service-worker.js +0 -0
  124. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/storage.js +0 -0
  125. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/storage.js.map +0 -0
  126. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/toml-BK2RWy-G.js +0 -0
  127. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/toml-BK2RWy-G.js.map +0 -0
  128. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/toml-Blg7Izee.js +0 -0
  129. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/toml-Blg7Izee.js.map +0 -0
  130. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm-DrSYbXEP.js +0 -0
  131. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm-DrSYbXEP.js.map +0 -0
  132. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm-readline-CK_45Ygx.js +0 -0
  133. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm-readline-CK_45Ygx.js.map +0 -0
  134. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm.css +0 -0
  135. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm_addon-fit--gyF3PcZ.js +0 -0
  136. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm_addon-fit--gyF3PcZ.js.map +0 -0
  137. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm_addon-web-links-D95xh2la.js +0 -0
  138. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/xterm_addon-web-links-D95xh2la.js.map +0 -0
  139. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/zip-pccs084i.js +0 -0
  140. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript/zip-pccs084i.js.map +0 -0
  141. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/static/pyscript-hide-debug.css +0 -0
  142. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/templatetags/__init__.py +0 -0
  143. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/testing/logs.py +0 -0
  144. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/transforms.py +0 -0
  145. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/types.py +0 -0
  146. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/utils.py +0 -0
  147. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/web/__init__.py +0 -0
  148. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/web/module.py +0 -0
  149. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/web/utils.py +0 -0
  150. {reactpy-2.0.0b6 → reactpy-2.0.0b8}/src/reactpy/widgets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactpy
3
- Version: 2.0.0b6
3
+ Version: 2.0.0b8
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/
@@ -1,6 +1,6 @@
1
1
  [build-system]
2
2
  build-backend = "hatchling.build"
3
- requires = ["hatchling", "hatch-build-scripts"]
3
+ requires = ["hatchling", "hatch-build-scripts", "hatch"]
4
4
 
5
5
  ##############################
6
6
  # >>> Hatch Build Config <<< #
@@ -71,28 +71,34 @@ installer = "uv"
71
71
  reactpy = "reactpy._console.cli:entry_point"
72
72
 
73
73
  [[tool.hatch.build.hooks.build-scripts.scripts]]
74
- # Note: `hatch` can't be called within `build-scripts` when installing packages in editable mode, so we have to write the commands long-form
75
- commands = [
76
- 'python "src/build_scripts/clean_js_dir.py"',
77
- 'bun install --cwd "src/js/packages/event-to-object"',
78
- 'bun run --cwd "src/js/packages/event-to-object" build',
79
- 'bun install --cwd "src/js/packages/@reactpy/client"',
80
- 'bun run --cwd "src/js/packages/@reactpy/client" build',
81
- 'bun install --cwd "src/js/packages/@reactpy/app"',
82
- 'bun run --cwd "src/js/packages/@reactpy/app" build',
83
- 'python "src/build_scripts/copy_dir.py" "src/js/node_modules/@pyscript/core/dist" "src/reactpy/static/pyscript"',
84
- 'python "src/build_scripts/copy_dir.py" "src/js/node_modules/morphdom/dist" "src/reactpy/static/morphdom"',
85
- ]
74
+ commands = []
86
75
  artifacts = []
87
76
 
88
77
  #############################
89
78
  # >>> Hatch Test Runner <<< #
90
79
  #############################
80
+ [tool.hatch.envs.hatch-test.scripts]
81
+ run = [
82
+ 'hatch --env default run "src/build_scripts/install_playwright.py"',
83
+ "hatch --env default run javascript:build --dev",
84
+ "hatch --env default build -t wheel",
85
+ "pytest{env:HATCH_TEST_ARGS:} {args} --max-worker-restart 10",
86
+ ]
87
+ run-cov = [
88
+ 'hatch --env default run "src/build_scripts/install_playwright.py"',
89
+ "hatch --env default run javascript:build --dev",
90
+ "hatch --env default build -t wheel",
91
+ 'hatch --env default run "src/build_scripts/delete_old_coverage.py"',
92
+ "coverage run -m pytest{env:HATCH_TEST_ARGS:} {args} --max-worker-restart 10",
93
+ ]
94
+ cov-combine = "coverage combine"
95
+ cov-report = "coverage report"
91
96
 
92
97
  [tool.hatch.envs.hatch-test]
93
98
  extra-dependencies = [
94
99
  "pytest-sugar",
95
100
  "pytest-asyncio",
101
+ "pytest-timeout",
96
102
  "responses",
97
103
  "exceptiongroup",
98
104
  "jsonpointer",
@@ -115,8 +121,10 @@ filterwarnings = """
115
121
  testpaths = "tests"
116
122
  xfail_strict = true
117
123
  asyncio_mode = "auto"
118
- asyncio_default_fixture_loop_scope = "function"
124
+ asyncio_default_fixture_loop_scope = "session"
125
+ asyncio_default_test_loop_scope = "session"
119
126
  log_cli_level = "INFO"
127
+ timeout = 120
120
128
 
121
129
  #######################################
122
130
  # >>> Hatch Documentation Scripts <<< #
@@ -162,6 +170,7 @@ extra-dependencies = [
162
170
  "types-requests",
163
171
  "types-lxml",
164
172
  "jsonpointer",
173
+ "pytest",
165
174
  ]
166
175
 
167
176
  [tool.hatch.envs.python.scripts]
@@ -177,40 +186,39 @@ detached = true
177
186
  [tool.hatch.envs.javascript.scripts]
178
187
  check = [
179
188
  'hatch run javascript:build',
180
- 'bun install --cwd "src/js"',
181
189
  'bun run --cwd "src/js" lint',
182
190
  'bun run --cwd "src/js/packages/event-to-object" checkTypes',
183
191
  'bun run --cwd "src/js/packages/@reactpy/client" checkTypes',
184
192
  'bun run --cwd "src/js/packages/@reactpy/app" checkTypes',
185
193
  ]
186
194
  fix = ['bun install --cwd "src/js"', 'bun run --cwd "src/js" format']
187
- test = ['hatch run javascript:build_event_to_object', 'bun test']
195
+ test = ['hatch run javascript:build_event_to_object --dev', 'bun test']
188
196
  build = [
189
197
  'hatch run "src/build_scripts/clean_js_dir.py"',
190
198
  'bun install --cwd "src/js"',
191
- 'hatch run javascript:build_event_to_object',
192
- 'hatch run javascript:build_client',
193
- 'hatch run javascript:build_app',
199
+ 'hatch run javascript:build_event_to_object {args}',
200
+ 'hatch run javascript:build_client {args}',
201
+ 'hatch run javascript:build_app {args}',
202
+ 'hatch --env default run "src/build_scripts/copy_dir.py" "src/js/node_modules/@pyscript/core/dist" "src/reactpy/static/pyscript"',
203
+ 'hatch --env default run "src/build_scripts/copy_dir.py" "src/js/node_modules/morphdom/dist" "src/reactpy/static/morphdom"',
204
+
194
205
  ]
195
206
  build_event_to_object = [
196
- 'bun install --cwd "src/js/packages/event-to-object"',
197
- 'bun run --cwd "src/js/packages/event-to-object" build',
198
- ]
199
- build_client = [
200
- 'bun install --cwd "src/js/packages/@reactpy/client"',
201
- 'bun run --cwd "src/js/packages/@reactpy/client" build',
202
- ]
203
- build_app = [
204
- 'bun install --cwd "src/js/packages/@reactpy/app"',
205
- 'bun run --cwd "src/js/packages/@reactpy/app" build',
207
+ 'hatch run "src/build_scripts/build_js_event_to_object.py" {args}',
206
208
  ]
209
+ build_client = ['hatch run "src/build_scripts/build_js_client.py" {args}']
210
+ build_app = ['hatch run "src/build_scripts/build_js_app.py" {args}']
207
211
  publish_event_to_object = [
208
212
  'hatch run javascript:build_event_to_object',
209
- 'cd "src/js/packages/event-to-object" && bun publish --access public',
213
+ # FIXME: using npm publish to fix lack of "Trusted Publishing" in Bun
214
+ # Ref: https://github.com/oven-sh/bun/issues/15601
215
+ 'cd "src/js/packages/event-to-object" && bunx npm@11.8.0 publish --provenance --access public',
210
216
  ]
211
217
  publish_client = [
212
218
  'hatch run javascript:build_client',
213
- 'cd "src/js/packages/@reactpy/client" && bun publish --access public',
219
+ # FIXME: using npm publish to fix lack of "Trusted Publishing" in Bun
220
+ # Ref: https://github.com/oven-sh/bun/issues/15601
221
+ 'cd "src/js/packages/@reactpy/client" && bunx npm@11.8.0 publish --provenance --access public',
214
222
  ]
215
223
 
216
224
  #########################
@@ -228,8 +236,8 @@ omit = [
228
236
  "src/reactpy/__init__.py",
229
237
  "src/reactpy/_console/*",
230
238
  "src/reactpy/__main__.py",
231
- "src/reactpy/pyscript/layout_handler.py",
232
- "src/reactpy/pyscript/component_template.py",
239
+ "src/reactpy/executors/pyscript/layout_handler.py",
240
+ "src/reactpy/executors/pyscript/component_template.py",
233
241
  ]
234
242
 
235
243
  [tool.coverage.report]
@@ -0,0 +1,29 @@
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = []
4
+ # ///
5
+ import pathlib
6
+ import subprocess
7
+ import sys
8
+
9
+ dev_mode = "--dev" in sys.argv
10
+ root_dir = pathlib.Path(__file__).parent.parent.parent
11
+ build_commands = [
12
+ [
13
+ "bun",
14
+ "install",
15
+ "--cwd",
16
+ "src/js/packages/@reactpy/app",
17
+ ],
18
+ [
19
+ "bun",
20
+ "run",
21
+ "--cwd",
22
+ "src/js/packages/@reactpy/app",
23
+ "buildDev" if dev_mode else "build",
24
+ ],
25
+ ]
26
+
27
+ for command in build_commands:
28
+ print(f"Running command: '{command}'...") # noqa: T201
29
+ subprocess.run(command, check=True, cwd=root_dir) # noqa: S603
@@ -0,0 +1,29 @@
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = []
4
+ # ///
5
+ import pathlib
6
+ import subprocess
7
+ import sys
8
+
9
+ dev_mode = "--dev" in sys.argv
10
+ root_dir = pathlib.Path(__file__).parent.parent.parent
11
+ build_commands = [
12
+ [
13
+ "bun",
14
+ "install",
15
+ "--cwd",
16
+ "src/js/packages/@reactpy/client",
17
+ ],
18
+ [
19
+ "bun",
20
+ "run",
21
+ "--cwd",
22
+ "src/js/packages/@reactpy/client",
23
+ "build",
24
+ ],
25
+ ]
26
+
27
+ for command in build_commands:
28
+ print(f"Running command: '{command}'...") # noqa: T201
29
+ subprocess.run(command, check=True, cwd=root_dir) # noqa: S603
@@ -0,0 +1,29 @@
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = []
4
+ # ///
5
+ import pathlib
6
+ import subprocess
7
+ import sys
8
+
9
+ dev_mode = "--dev" in sys.argv
10
+ root_dir = pathlib.Path(__file__).parent.parent.parent
11
+ build_commands = [
12
+ [
13
+ "bun",
14
+ "install",
15
+ "--cwd",
16
+ "src/js/packages/event-to-object",
17
+ ],
18
+ [
19
+ "bun",
20
+ "run",
21
+ "--cwd",
22
+ "src/js/packages/event-to-object",
23
+ "build",
24
+ ],
25
+ ]
26
+
27
+ for command in build_commands:
28
+ print(f"Running command: '{command}'...") # noqa: T201
29
+ subprocess.run(command, check=True, cwd=root_dir) # noqa: S603
@@ -11,6 +11,8 @@ import os
11
11
  import pathlib
12
12
  import shutil
13
13
 
14
+ print("Cleaning JS source directory...") # noqa: T201
15
+
14
16
  # Get the path to the JS source directory
15
17
  js_src_dir = pathlib.Path(__file__).parent.parent / "js"
16
18
  static_output_dir = pathlib.Path(__file__).parent.parent / "reactpy" / "static"
@@ -31,6 +31,7 @@ if __name__ == "__main__":
31
31
  root_dir = Path(__file__).parent.parent.parent
32
32
  src = Path(root_dir / sys.argv[1])
33
33
  dest = Path(root_dir / sys.argv[2])
34
+ print(f"Copying files from '{sys.argv[1]}' to '{sys.argv[2]}'...") # noqa: T201
34
35
 
35
36
  if not src.exists():
36
37
  logging.error("Source directory %s does not exist", src)
@@ -0,0 +1,21 @@
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = []
4
+ # ///
5
+
6
+ import logging
7
+ from glob import glob
8
+ from pathlib import Path
9
+
10
+ # Delete old `.coverage*` files in the project root
11
+ print("Deleting old coverage files...") # noqa: T201
12
+ root_dir = Path(__file__).parent.parent.parent
13
+ coverage_files = glob(str(root_dir / ".coverage*"))
14
+
15
+ for path in coverage_files:
16
+ coverage_file = Path(path)
17
+ if coverage_file.exists():
18
+ try:
19
+ coverage_file.unlink()
20
+ except Exception as e:
21
+ logging.error(f"Failed to delete {coverage_file}: {e}")
@@ -0,0 +1,17 @@
1
+ # /// script
2
+ # requires-python = ">=3.11"
3
+ # dependencies = []
4
+ # ///
5
+
6
+ import subprocess
7
+
8
+ print("Installing Playwright browsers...") # noqa: T201
9
+
10
+ # Install Chromium browser for Playwright, and fail if it cannot be installed
11
+ subprocess.run(["playwright", "install", "chromium"], check=True) # noqa: S607
12
+
13
+ # Try to install system dependencies. We don't generate an exception if this fails
14
+ # as *nix systems (such as WSL) return a failure code if there are *any* dependencies
15
+ # that could be cleaned up via `sudo apt autoremove`. This occurs even if we weren't
16
+ # the ones to install those dependencies in the first place.
17
+ subprocess.run(["playwright", "install-deps", "chromium"], check=False) # noqa: S607
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "workspaces": [
3
- "packages/*",
4
- "packages/@reactpy/*"
3
+ "packages/event-to-object",
4
+ "packages/@reactpy/app",
5
+ "packages/@reactpy/client"
5
6
  ],
6
7
  "catalog": {
7
8
  "preact": "^10.27.2",
@@ -15,12 +15,6 @@
15
15
  "moduleResolution": "bundler",
16
16
  "noEmitOnError": true,
17
17
  "noUnusedLocals": true,
18
- "paths": {
19
- "react": ["./node_modules/preact/compat/"],
20
- "react-dom": ["./node_modules/preact/compat/"],
21
- "react-dom/*": ["./node_modules/preact/compat/*"],
22
- "react/jsx-runtime": ["./node_modules/preact/jsx-runtime"]
23
- },
24
18
  "resolveJsonModule": true,
25
19
  "skipLibCheck": true,
26
20
  "sourceMap": true,
@@ -19,11 +19,11 @@ from reactpy.core.hooks import (
19
19
  use_state,
20
20
  )
21
21
  from reactpy.core.vdom import Vdom
22
- from reactpy.pyscript.components import pyscript_component
22
+ from reactpy.executors.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.0b6"
26
+ __version__ = "2.0.0b8"
27
27
 
28
28
  __all__ = [
29
29
  "Ref",
@@ -77,7 +77,7 @@ set of publicly available APIs for working with the client.
77
77
 
78
78
  REACTPY_TESTS_DEFAULT_TIMEOUT = Option(
79
79
  "REACTPY_TESTS_DEFAULT_TIMEOUT",
80
- 10.0,
80
+ 15.0,
81
81
  mutable=False,
82
82
  validator=float,
83
83
  )
@@ -8,7 +8,7 @@ _StateType = TypeVar("_StateType")
8
8
 
9
9
  class ThreadLocal(Generic[_StateType]): # nocov
10
10
  """Utility for managing per-thread state information. This is only used in
11
- environments where ContextVars are not available, such as the `pyodide`
11
+ environments where ContextVars are not available, such as the `pyscript`
12
12
  executor."""
13
13
 
14
14
  def __init__(self, default: Callable[[], _StateType]):
@@ -84,11 +84,7 @@ class _CurrentState(Generic[_Type]):
84
84
  self,
85
85
  initial_value: _Type | Callable[[], _Type],
86
86
  ) -> None:
87
- if callable(initial_value):
88
- self.value = initial_value()
89
- else:
90
- self.value = initial_value
91
-
87
+ self.value = initial_value() if callable(initial_value) else initial_value
92
88
  hook = HOOK_STACK.current_hook()
93
89
 
94
90
  def dispatch(new: _Type | Callable[[_Type], _Type]) -> None:
@@ -186,7 +182,6 @@ def use_effect(
186
182
  def use_async_effect(
187
183
  function: None = None,
188
184
  dependencies: Sequence[Any] | ellipsis | None = ...,
189
- shutdown_timeout: float = 0.1,
190
185
  ) -> Callable[[_EffectApplyFunc], None]: ...
191
186
 
192
187
 
@@ -194,14 +189,12 @@ def use_async_effect(
194
189
  def use_async_effect(
195
190
  function: _AsyncEffectFunc,
196
191
  dependencies: Sequence[Any] | ellipsis | None = ...,
197
- shutdown_timeout: float = 0.1,
198
192
  ) -> None: ...
199
193
 
200
194
 
201
195
  def use_async_effect(
202
196
  function: _AsyncEffectFunc | None = None,
203
197
  dependencies: Sequence[Any] | ellipsis | None = ...,
204
- shutdown_timeout: float = 0.1,
205
198
  ) -> Callable[[_AsyncEffectFunc], None] | None:
206
199
  """
207
200
  A hook that manages an asynchronous side effect in a React-like component.
@@ -218,9 +211,6 @@ def use_async_effect(
218
211
  of any value in the given sequence changes (i.e. their :func:`id` is
219
212
  different). By default these are inferred based on local variables that are
220
213
  referenced by the given function.
221
- shutdown_timeout:
222
- The amount of time (in seconds) to wait for the effect to complete before
223
- forcing a shutdown.
224
214
 
225
215
  Returns:
226
216
  If not function is provided, a decorator. Otherwise ``None``.
@@ -236,26 +226,37 @@ def use_async_effect(
236
226
  # always clean up the previous effect's resources
237
227
  run_effect_cleanup(cleanup_func)
238
228
 
239
- # Execute the effect in a background task
229
+ # Execute the effect and store the clean-up function.
230
+ # We run this in a task so it can be cancelled if the stop signal
231
+ # is set before the effect completes.
240
232
  task = asyncio.create_task(func())
241
233
 
242
- # Wait until we get the signal to stop this effect
243
- await stop.wait()
234
+ # Wait for either the effect to complete or the stop signal
235
+ stop_task = asyncio.create_task(stop.wait())
236
+ done, _ = await asyncio.wait(
237
+ [task, stop_task],
238
+ return_when=asyncio.FIRST_COMPLETED,
239
+ )
244
240
 
245
- # If renders are queued back-to-back, the effect might not have
246
- # completed. So, we give the task a small amount of time to finish.
247
- # If it manages to finish, we can obtain a clean-up function.
248
- results, _ = await asyncio.wait([task], timeout=shutdown_timeout)
249
- if results:
250
- cleanup_func.current = results.pop().result()
241
+ # If the effect completed first, store the cleanup function
242
+ if task in done:
243
+ cleanup_func.current = task.result()
244
+ # Cancel the stop task since we don't need it anymore
245
+ stop_task.cancel()
246
+ with contextlib.suppress(asyncio.CancelledError):
247
+ await stop_task
248
+ # Now wait for the stop signal to run cleanup
249
+ await stop.wait()
250
+ else:
251
+ # Stop signal came first - cancel the effect task
252
+ task.cancel()
253
+ with contextlib.suppress(asyncio.CancelledError):
254
+ await task
251
255
 
252
256
  # Run the clean-up function when the effect is stopped,
253
257
  # if it hasn't been run already by a new effect
254
258
  run_effect_cleanup(cleanup_func)
255
259
 
256
- # Cancel the task if it's still running
257
- task.cancel()
258
-
259
260
  return memoize(lambda: hook.add_effect(effect))
260
261
 
261
262
  # Handle decorator usage
@@ -434,10 +435,7 @@ def use_callback(
434
435
  def setup(function: _CallbackFunc) -> _CallbackFunc:
435
436
  return memoize(lambda: function)
436
437
 
437
- if function is not None:
438
- return setup(function)
439
- else:
440
- return setup
438
+ return setup(function) if function is not None else setup
441
439
 
442
440
 
443
441
  class _LambdaCaller(Protocol):
@@ -553,17 +551,16 @@ def _try_to_infer_closure_values(
553
551
  func: Callable[..., Any] | None,
554
552
  values: Sequence[Any] | ellipsis | None,
555
553
  ) -> Sequence[Any] | None:
556
- if values is ...:
557
- if isinstance(func, FunctionType):
558
- return (
559
- [cell.cell_contents for cell in func.__closure__]
560
- if func.__closure__
561
- else []
562
- )
563
- else:
564
- return None
565
- else:
554
+ if values is not ...:
566
555
  return values
556
+ if isinstance(func, FunctionType):
557
+ return (
558
+ [cell.cell_contents for cell in func.__closure__]
559
+ if func.__closure__
560
+ else []
561
+ )
562
+ else:
563
+ return None
567
564
 
568
565
 
569
566
  def strictly_equal(x: Any, y: Any) -> bool:
@@ -13,8 +13,11 @@ from reactpy import html
13
13
  from reactpy.executors.asgi.middleware import ReactPyMiddleware
14
14
  from reactpy.executors.asgi.standalone import ReactPy, ReactPyApp
15
15
  from reactpy.executors.asgi.types import AsgiWebsocketScope
16
+ from reactpy.executors.pyscript.utils import (
17
+ pyscript_component_html,
18
+ pyscript_setup_html,
19
+ )
16
20
  from reactpy.executors.utils import vdom_head_to_html
17
- from reactpy.pyscript.utils import pyscript_component_html, pyscript_setup_html
18
21
  from reactpy.types import ReactPyConfig, VdomDict
19
22
 
20
23
 
@@ -23,8 +23,8 @@ from reactpy.executors.asgi.types import (
23
23
  AsgiV3WebsocketApp,
24
24
  AsgiWebsocketScope,
25
25
  )
26
+ from reactpy.executors.pyscript.utils import pyscript_setup_html
26
27
  from reactpy.executors.utils import server_side_component_html, vdom_head_to_html
27
- from reactpy.pyscript.utils import pyscript_setup_html
28
28
  from reactpy.types import (
29
29
  PyScriptOptions,
30
30
  ReactPyConfig,
@@ -2,7 +2,7 @@
2
2
  # type: ignore
3
3
  import asyncio
4
4
 
5
- from reactpy.pyscript.layout_handler import ReactPyLayoutHandler
5
+ from reactpy.executors.pyscript.layout_handler import ReactPyLayoutHandler
6
6
 
7
7
 
8
8
  # User component is inserted below by regex replacement
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from typing import TYPE_CHECKING
5
5
 
6
6
  from reactpy import component, hooks
7
- from reactpy.pyscript.utils import pyscript_component_html
7
+ from reactpy.executors.pyscript.utils import pyscript_component_html
8
8
  from reactpy.types import Component, Key
9
9
  from reactpy.utils import string_to_reactpy
10
10
 
@@ -64,16 +64,41 @@ def server_side_component_html(
64
64
  ) -> str:
65
65
  return (
66
66
  f'<div id="{element_id}" class="{class_}"></div>'
67
+ "<script>"
68
+ 'if (!document.querySelector("#reactpy-importmap")) {'
69
+ " console.debug("
70
+ ' "ReactPy did not detect a suitable JavaScript import map. Falling back to ReactPy\'s internal framework (Preact)."'
71
+ " );"
72
+ ' const im = document.createElement("script");'
73
+ ' im.type = "importmap";'
74
+ f" im.textContent = '{default_import_map()}';"
75
+ ' im.id = "reactpy-importmap";'
76
+ " document.head.appendChild(im);"
77
+ " delete im;"
78
+ "}"
79
+ "</script>"
67
80
  '<script type="module" crossorigin="anonymous">'
68
81
  f'import {{ mountReactPy }} from "{REACTPY_PATH_PREFIX.current}static/index.js";'
69
82
  "mountReactPy({"
70
- f' mountElement: document.getElementById("{element_id}"),'
71
- f' pathPrefix: "{REACTPY_PATH_PREFIX.current}",'
72
- f' componentPath: "{component_path}",'
73
- f" reconnectInterval: {REACTPY_RECONNECT_INTERVAL.current},"
74
- f" reconnectMaxInterval: {REACTPY_RECONNECT_MAX_INTERVAL.current},"
75
- f" reconnectMaxRetries: {REACTPY_RECONNECT_MAX_RETRIES.current},"
76
- f" reconnectBackoffMultiplier: {REACTPY_RECONNECT_BACKOFF_MULTIPLIER.current},"
83
+ f' mountElement: document.getElementById("{element_id}"),'
84
+ f' pathPrefix: "{REACTPY_PATH_PREFIX.current}",'
85
+ f' componentPath: "{component_path}",'
86
+ f" reconnectInterval: {REACTPY_RECONNECT_INTERVAL.current},"
87
+ f" reconnectMaxInterval: {REACTPY_RECONNECT_MAX_INTERVAL.current},"
88
+ f" reconnectMaxRetries: {REACTPY_RECONNECT_MAX_RETRIES.current},"
89
+ f" reconnectBackoffMultiplier: {REACTPY_RECONNECT_BACKOFF_MULTIPLIER.current},"
77
90
  "});"
78
91
  "</script>"
79
92
  )
93
+
94
+
95
+ def default_import_map() -> str:
96
+ path_prefix = REACTPY_PATH_PREFIX.current.strip("/")
97
+ return f"""{{
98
+ "imports": {{
99
+ "react": "/{path_prefix}/static/preact.js",
100
+ "react-dom": "/{path_prefix}/static/preact-dom.js",
101
+ "react-dom/client": "/{path_prefix}/static/preact-dom.js",
102
+ "react/jsx-runtime": "/{path_prefix}/static/preact-jsx-runtime.js"
103
+ }}
104
+ }}""".replace("\n", "").replace(" ", "")
@@ -110,7 +110,7 @@ def component_from_npm(
110
110
  resolve_imports: bool = ...,
111
111
  resolve_imports_depth: int = ...,
112
112
  version: str = "latest",
113
- cdn: str = "https://esm.sh",
113
+ cdn: str = "https://esm.sh/v135",
114
114
  fallback: Any | None = ...,
115
115
  unmount_before_update: bool = ...,
116
116
  allow_children: bool = ...,
@@ -124,7 +124,7 @@ def component_from_npm(
124
124
  resolve_imports: bool = ...,
125
125
  resolve_imports_depth: int = ...,
126
126
  version: str = "latest",
127
- cdn: str = "https://esm.sh",
127
+ cdn: str = "https://esm.sh/v135",
128
128
  fallback: Any | None = ...,
129
129
  unmount_before_update: bool = ...,
130
130
  allow_children: bool = ...,
@@ -137,7 +137,7 @@ def component_from_npm(
137
137
  resolve_imports: bool = False,
138
138
  resolve_imports_depth: int = 5,
139
139
  version: str = "latest",
140
- cdn: str = "https://esm.sh",
140
+ cdn: str = "https://esm.sh/v135",
141
141
  fallback: Any | None = None,
142
142
  unmount_before_update: bool = False,
143
143
  allow_children: bool = True,
@@ -175,10 +175,8 @@ def component_from_npm(
175
175
  url = f"{cdn}/{package}@{version}"
176
176
 
177
177
  if "esm.sh" in cdn:
178
- if "?" in url:
179
- url += "&external=react,react-dom"
180
- else:
181
- url += "?external=react,react-dom"
178
+ url += "&" if "?" in url else "?"
179
+ url += "external=react,react-dom,react/jsx-runtime&bundle&target=es2020"
182
180
 
183
181
  return component_from_url(
184
182
  url,